outpost: separate ak-api and proxy further for future outposts
This commit is contained in:
parent
87b830ff9a
commit
8fef839965
|
@ -1,4 +1,4 @@
|
||||||
package cmd
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
@ -8,18 +8,21 @@ import (
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/BeryJu/authentik/outpost/pkg/server"
|
log "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
|
"github.com/BeryJu/authentik/outpost/pkg/ak"
|
||||||
|
"github.com/BeryJu/authentik/outpost/pkg/proxy"
|
||||||
)
|
)
|
||||||
|
|
||||||
const helpMessage = `authentik proxy
|
const helpMessage = `authentik proxy
|
||||||
|
|
||||||
Required environment variables:
|
Required environment variables:
|
||||||
- AUTHENTIK_HOST: URL to connect to (format "http://authentik.company")
|
- AUTHENTIK_HOST: URL to connect to (format "http://authentik.company")
|
||||||
- AUTHENTIK_TOKEN: Token to authenticate with
|
- AUTHENTIK_TOKEN: Token to authenticate with
|
||||||
- AUTHENTIK_INSECURE: Skip SSL Certificate verification`
|
- AUTHENTIK_INSECURE: Skip SSL Certificate verification`
|
||||||
|
|
||||||
// RunServer main entrypoint, runs the full server
|
func main() {
|
||||||
func RunServer() {
|
log.SetLevel(log.DebugLevel)
|
||||||
pbURL, found := os.LookupEnv("AUTHENTIK_HOST")
|
pbURL, found := os.LookupEnv("AUTHENTIK_HOST")
|
||||||
if !found {
|
if !found {
|
||||||
fmt.Println("env AUTHENTIK_HOST not set!")
|
fmt.Println("env AUTHENTIK_HOST not set!")
|
||||||
|
@ -42,11 +45,13 @@ func RunServer() {
|
||||||
|
|
||||||
rand.Seed(time.Now().UnixNano())
|
rand.Seed(time.Now().UnixNano())
|
||||||
|
|
||||||
ac := server.NewAPIController(*pbURLActual, pbToken)
|
ac := ak.NewAPIController(*pbURLActual, pbToken)
|
||||||
|
|
||||||
interrupt := make(chan os.Signal, 1)
|
interrupt := make(chan os.Signal, 1)
|
||||||
signal.Notify(interrupt, os.Interrupt)
|
signal.Notify(interrupt, os.Interrupt)
|
||||||
|
|
||||||
|
ac.Server = proxy.NewServer(ac)
|
||||||
|
|
||||||
ac.Start()
|
ac.Start()
|
||||||
|
|
||||||
for {
|
for {
|
|
@ -4,6 +4,7 @@ go 1.14
|
||||||
|
|
||||||
require (
|
require (
|
||||||
cloud.google.com/go v0.64.0 // indirect
|
cloud.google.com/go v0.64.0 // indirect
|
||||||
|
github.com/BeryJu/authentik/proxy v0.0.0-20210116180903-8acb9dde5f2f
|
||||||
github.com/coreos/go-oidc v2.2.1+incompatible
|
github.com/coreos/go-oidc v2.2.1+incompatible
|
||||||
github.com/getsentry/sentry-go v0.9.0
|
github.com/getsentry/sentry-go v0.9.0
|
||||||
github.com/go-openapi/errors v0.19.9
|
github.com/go-openapi/errors v0.19.9
|
||||||
|
@ -19,8 +20,10 @@ require (
|
||||||
github.com/justinas/alice v1.2.0
|
github.com/justinas/alice v1.2.0
|
||||||
github.com/kr/pretty v0.2.1 // indirect
|
github.com/kr/pretty v0.2.1 // indirect
|
||||||
github.com/magiconair/properties v1.8.4 // indirect
|
github.com/magiconair/properties v1.8.4 // indirect
|
||||||
|
github.com/mitchellh/mapstructure v1.4.1 // indirect
|
||||||
github.com/oauth2-proxy/oauth2-proxy v0.0.0-20200831161845-e4e5580852dc
|
github.com/oauth2-proxy/oauth2-proxy v0.0.0-20200831161845-e4e5580852dc
|
||||||
github.com/pelletier/go-toml v1.8.1 // indirect
|
github.com/pelletier/go-toml v1.8.1 // indirect
|
||||||
|
github.com/pkg/errors v0.9.1
|
||||||
github.com/pquerna/cachecontrol v0.0.0-20200819021114-67c6ae64274f // indirect
|
github.com/pquerna/cachecontrol v0.0.0-20200819021114-67c6ae64274f // indirect
|
||||||
github.com/recws-org/recws v1.2.2
|
github.com/recws-org/recws v1.2.2
|
||||||
github.com/sirupsen/logrus v1.7.0
|
github.com/sirupsen/logrus v1.7.0
|
||||||
|
@ -30,11 +33,11 @@ require (
|
||||||
github.com/spf13/pflag v1.0.5 // indirect
|
github.com/spf13/pflag v1.0.5 // indirect
|
||||||
github.com/spf13/viper v1.7.1 // indirect
|
github.com/spf13/viper v1.7.1 // indirect
|
||||||
golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de // indirect
|
golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de // indirect
|
||||||
golang.org/x/mod v0.4.0 // indirect
|
golang.org/x/mod v0.4.1 // indirect
|
||||||
golang.org/x/net v0.0.0-20201224014010-6772e930b67b // indirect
|
golang.org/x/net v0.0.0-20201224014010-6772e930b67b // indirect
|
||||||
golang.org/x/sys v0.0.0-20210108172913-0df2131ae363 // indirect
|
golang.org/x/sys v0.0.0-20210113181707-4bcb84eeeb78 // indirect
|
||||||
golang.org/x/text v0.3.5 // indirect
|
golang.org/x/text v0.3.5 // indirect
|
||||||
golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e // indirect
|
golang.org/x/tools v0.0.0-20210115202250-e0d201561e39 // indirect
|
||||||
gopkg.in/ini.v1 v1.62.0 // indirect
|
gopkg.in/ini.v1 v1.62.0 // indirect
|
||||||
gopkg.in/square/go-jose.v2 v2.5.1 // indirect
|
gopkg.in/square/go-jose.v2 v2.5.1 // indirect
|
||||||
)
|
)
|
||||||
|
|
|
@ -36,8 +36,11 @@ cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9
|
||||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||||
github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8=
|
github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8=
|
||||||
github.com/BeryJu/authentik v0.0.0-20210108085217-fd6d99f4f999 h1:ymxzvnxKNUomJIRG1VP3I6ls5mWn8r1xWD82bHASEk0=
|
github.com/BeryJu/authentik v0.0.0-20210108085217-fd6d99f4f999 h1:ymxzvnxKNUomJIRG1VP3I6ls5mWn8r1xWD82bHASEk0=
|
||||||
|
github.com/BeryJu/authentik v0.0.0-20210116180903-8acb9dde5f2f h1:pLOJgn8bIzavtn0h874lys3gs7uk1RnMqMWIttOWY8Y=
|
||||||
github.com/BeryJu/authentik/outpost v0.0.0-20210108085217-fd6d99f4f999 h1:XYHeaZx7fm4JNx77MHMO6ek/Gdp+sZa2jIJyjC294Gw=
|
github.com/BeryJu/authentik/outpost v0.0.0-20210108085217-fd6d99f4f999 h1:XYHeaZx7fm4JNx77MHMO6ek/Gdp+sZa2jIJyjC294Gw=
|
||||||
github.com/BeryJu/authentik/proxy v0.0.0-20210108085217-fd6d99f4f999 h1:XYHeaZx7fm4JNx77MHMO6ek/Gdp+sZa2jIJyjC294Gw=
|
github.com/BeryJu/authentik/proxy v0.0.0-20210108085217-fd6d99f4f999 h1:XYHeaZx7fm4JNx77MHMO6ek/Gdp+sZa2jIJyjC294Gw=
|
||||||
|
github.com/BeryJu/authentik/proxy v0.0.0-20210116180903-8acb9dde5f2f h1:bp617AbteaVcZBXMtr4/A+FSSVGKqRWlTo5chcirq8k=
|
||||||
|
github.com/BeryJu/authentik/proxy v0.0.0-20210116180903-8acb9dde5f2f/go.mod h1:6/VeRMuLHUE3Ywr1uIpjxnmOJJsAfld7OOOi+uocxQw=
|
||||||
github.com/Bose/minisentinel v0.0.0-20200130220412-917c5a9223bb h1:ZVN4Iat3runWOFLaBCDVU5a9X/XikSRBosye++6gojw=
|
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/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 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
||||||
|
@ -508,6 +511,8 @@ github.com/mitchellh/mapstructure v1.3.3 h1:SzB1nHZ2Xi+17FP0zVQBHIZqvwRN9408fJO8
|
||||||
github.com/mitchellh/mapstructure v1.3.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
github.com/mitchellh/mapstructure v1.3.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||||
github.com/mitchellh/mapstructure v1.4.0 h1:7ks8ZkOP5/ujthUsT07rNv+nkLXCQWKNHuwzOAesEks=
|
github.com/mitchellh/mapstructure v1.4.0 h1:7ks8ZkOP5/ujthUsT07rNv+nkLXCQWKNHuwzOAesEks=
|
||||||
github.com/mitchellh/mapstructure v1.4.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
github.com/mitchellh/mapstructure v1.4.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||||
|
github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag=
|
||||||
|
github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||||
|
@ -752,6 +757,8 @@ golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4=
|
||||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/mod v0.4.0 h1:8pl+sMODzuvGJkmj2W4kZihvVb5mKm8pB/X44PIQHv8=
|
golang.org/x/mod v0.4.0 h1:8pl+sMODzuvGJkmj2W4kZihvVb5mKm8pB/X44PIQHv8=
|
||||||
golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
|
golang.org/x/mod v0.4.1 h1:Kvvh58BN8Y9/lBi7hTekvtMpm07eUZ0ck5pRHpsMWrY=
|
||||||
|
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
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-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
@ -880,6 +887,8 @@ golang.org/x/sys v0.0.0-20201223074533-0d417f636930 h1:vRgIt+nup/B/BwIS0g2oC0haq
|
||||||
golang.org/x/sys v0.0.0-20201223074533-0d417f636930/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201223074533-0d417f636930/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210108172913-0df2131ae363 h1:wHn06sgWHMO1VsQ8F+KzDJx/JzqfsNLnc+oEi07qD7s=
|
golang.org/x/sys v0.0.0-20210108172913-0df2131ae363 h1:wHn06sgWHMO1VsQ8F+KzDJx/JzqfsNLnc+oEi07qD7s=
|
||||||
golang.org/x/sys v0.0.0-20210108172913-0df2131ae363/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210108172913-0df2131ae363/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210113181707-4bcb84eeeb78 h1:nVuTkr9L6Bq62qpUqKo/RnZCFfzDBL0bYo6w9OJUqZY=
|
||||||
|
golang.org/x/sys v0.0.0-20210113181707-4bcb84eeeb78/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
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.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
@ -955,6 +964,8 @@ golang.org/x/tools v0.0.0-20201226215659-b1c90890d22a h1:pdfjQ7VswBeGam3EpuEJ4e8
|
||||||
golang.org/x/tools v0.0.0-20201226215659-b1c90890d22a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
golang.org/x/tools v0.0.0-20201226215659-b1c90890d22a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||||
golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e h1:Z2uDrs8MyXUWJbwGc4V+nGjV4Ygo+oubBbWSVQw21/I=
|
golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e h1:Z2uDrs8MyXUWJbwGc4V+nGjV4Ygo+oubBbWSVQw21/I=
|
||||||
golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||||
|
golang.org/x/tools v0.0.0-20210115202250-e0d201561e39 h1:BTs2GMGSMWpgtCpv1CE7vkJTv7XcHdcLLnAMu7UbgTY=
|
||||||
|
golang.org/x/tools v0.0.0-20210115202250-e0d201561e39/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7 h1:9zdDQZ7Thm29KFXgAX/+yaf3eVbP7djjWp/dXAppNCc=
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7 h1:9zdDQZ7Thm29KFXgAX/+yaf3eVbP7djjWp/dXAppNCc=
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
|
|
@ -1,11 +1,5 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/BeryJu/authentik/outpost/cmd"
|
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
log.SetLevel(log.DebugLevel)
|
|
||||||
cmd.RunServer()
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,100 @@
|
||||||
|
package ak
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"math/rand"
|
||||||
|
"net/url"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/BeryJu/authentik/outpost/pkg"
|
||||||
|
"github.com/BeryJu/authentik/outpost/pkg/client"
|
||||||
|
"github.com/BeryJu/authentik/outpost/pkg/client/outposts"
|
||||||
|
"github.com/go-openapi/runtime"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"github.com/recws-org/recws"
|
||||||
|
|
||||||
|
httptransport "github.com/go-openapi/runtime/client"
|
||||||
|
"github.com/go-openapi/strfmt"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
const ConfigLogLevel = "log_level"
|
||||||
|
const ConfigErrorReportingEnabled = "error_reporting_enabled"
|
||||||
|
const ConfigErrorReportingEnvironment = "error_reporting_environment"
|
||||||
|
|
||||||
|
// APIController main controller which connects to the authentik api via http and ws
|
||||||
|
type APIController struct {
|
||||||
|
Client *client.Authentik
|
||||||
|
Auth runtime.ClientAuthInfoWriter
|
||||||
|
token string
|
||||||
|
|
||||||
|
Server Outpost
|
||||||
|
|
||||||
|
lastBundleHash string
|
||||||
|
logger *log.Entry
|
||||||
|
|
||||||
|
reloadOffset time.Duration
|
||||||
|
|
||||||
|
wsConn *recws.RecConn
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewAPIController initialise new API Controller instance from URL and API token
|
||||||
|
func NewAPIController(pbURL url.URL, token string) *APIController {
|
||||||
|
transport := httptransport.New(pbURL.Host, client.DefaultBasePath, []string{pbURL.Scheme})
|
||||||
|
transport.Transport = SetUserAgent(getTLSTransport(), fmt.Sprintf("authentik-proxy@%s", pkg.VERSION))
|
||||||
|
|
||||||
|
// create the transport
|
||||||
|
auth := httptransport.BasicAuth("", token)
|
||||||
|
|
||||||
|
// create the API client, with the transport
|
||||||
|
apiClient := client.New(transport, strfmt.Default)
|
||||||
|
|
||||||
|
// Because we don't know the outpost UUID, we simply do a list and pick the first
|
||||||
|
// The service account this token belongs to should only have access to a single outpost
|
||||||
|
outposts, err := apiClient.Outposts.OutpostsOutpostsList(outposts.NewOutpostsOutpostsListParams(), auth)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
outpost := outposts.Payload.Results[0]
|
||||||
|
doGlobalSetup(outpost.Config.(map[string]interface{}))
|
||||||
|
|
||||||
|
ac := &APIController{
|
||||||
|
Client: apiClient,
|
||||||
|
Auth: auth,
|
||||||
|
token: token,
|
||||||
|
|
||||||
|
logger: log.WithField("component", "ak-api-controller"),
|
||||||
|
|
||||||
|
reloadOffset: time.Duration(rand.Intn(10)) * time.Second,
|
||||||
|
|
||||||
|
lastBundleHash: "",
|
||||||
|
}
|
||||||
|
ac.logger.Debugf("HA Reload offset: %s", ac.reloadOffset)
|
||||||
|
ac.initWS(pbURL, outpost.Pk)
|
||||||
|
return ac
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *APIController) GetLastBundleHash() string {
|
||||||
|
return a.lastBundleHash
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start Starts all handlers, non-blocking
|
||||||
|
func (a *APIController) Start() error {
|
||||||
|
err := a.Server.Refresh()
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "failed to run initial refresh")
|
||||||
|
}
|
||||||
|
go func() {
|
||||||
|
a.logger.Debug("Starting WS Handler...")
|
||||||
|
a.startWSHandler()
|
||||||
|
}()
|
||||||
|
go func() {
|
||||||
|
a.logger.Debug("Starting WS Health notifier...")
|
||||||
|
a.startWSHealth()
|
||||||
|
}()
|
||||||
|
go func() {
|
||||||
|
a.Server.Start()
|
||||||
|
}()
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package server
|
package ak
|
||||||
|
|
||||||
import "net/http"
|
import "net/http"
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
package ak
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/sha512"
|
||||||
|
"encoding/hex"
|
||||||
|
|
||||||
|
"github.com/BeryJu/authentik/outpost/pkg/client/outposts"
|
||||||
|
"github.com/BeryJu/authentik/outpost/pkg/models"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (a *APIController) Update() ([]*models.ProxyOutpostConfig, error) {
|
||||||
|
providers, err := a.Client.Outposts.OutpostsProxyList(outposts.NewOutpostsProxyListParams(), a.Auth)
|
||||||
|
if err != nil {
|
||||||
|
a.logger.WithError(err).Error("Failed to fetch providers")
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// Check provider hash to see if anything is changed
|
||||||
|
hasher := sha512.New()
|
||||||
|
bin, _ := providers.Payload.MarshalBinary()
|
||||||
|
hash := hex.EncodeToString(hasher.Sum(bin))
|
||||||
|
if hash == a.lastBundleHash {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
a.lastBundleHash = hash
|
||||||
|
return providers.Payload.Results, nil
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package server
|
package ak
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
|
@ -40,7 +40,7 @@ func (ac *APIController) initWS(pbURL url.URL, outpostUUID strfmt.UUID) {
|
||||||
}
|
}
|
||||||
ws.Dial(fmt.Sprintf(pathTemplate, scheme, pbURL.Host, outpostUUID.String()), header)
|
ws.Dial(fmt.Sprintf(pathTemplate, scheme, pbURL.Host, outpostUUID.String()), header)
|
||||||
|
|
||||||
ac.logger.WithField("component", "ws").WithField("outpost", outpostUUID.String()).Debug("connecting to authentik")
|
ac.logger.WithField("component", "ak-ws").WithField("outpost", outpostUUID.String()).Debug("connecting to authentik")
|
||||||
|
|
||||||
ac.wsConn = ws
|
ac.wsConn = ws
|
||||||
// Send hello message with our version
|
// Send hello message with our version
|
||||||
|
@ -52,7 +52,7 @@ func (ac *APIController) initWS(pbURL url.URL, outpostUUID strfmt.UUID) {
|
||||||
}
|
}
|
||||||
err := ws.WriteJSON(msg)
|
err := ws.WriteJSON(msg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ac.logger.WithField("component", "ws").WithError(err).Warning("Failed to hello to authentik")
|
ac.logger.WithField("component", "ak-ws").WithError(err).Warning("Failed to hello to authentik")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -87,7 +87,7 @@ func (ac *APIController) startWSHandler() {
|
||||||
}
|
}
|
||||||
if wsMsg.Instruction == WebsocketInstructionTriggerUpdate {
|
if wsMsg.Instruction == WebsocketInstructionTriggerUpdate {
|
||||||
time.Sleep(ac.reloadOffset)
|
time.Sleep(ac.reloadOffset)
|
||||||
err := ac.UpdateIfRequired()
|
err := ac.Server.Refresh()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ac.logger.WithField("loop", "ws-handler").WithError(err).Debug("Failed to update")
|
ac.logger.WithField("loop", "ws-handler").WithError(err).Debug("Failed to update")
|
||||||
}
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package server
|
package ak
|
||||||
|
|
||||||
type websocketInstruction int
|
type websocketInstruction int
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package server
|
package ak
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
|
@ -13,8 +13,8 @@ import (
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
func generateSelfSignedCert() (tls.Certificate, error) {
|
// GenerateSelfSignedCert Generate a self-signed TLS Certificate, to be used as fallback
|
||||||
|
func GenerateSelfSignedCert() (tls.Certificate, error) {
|
||||||
priv, err := rsa.GenerateKey(rand.Reader, 2048)
|
priv, err := rsa.GenerateKey(rand.Reader, 2048)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Failed to generate private key: %v", err)
|
log.Fatalf("Failed to generate private key: %v", err)
|
|
@ -0,0 +1,60 @@
|
||||||
|
package ak
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/BeryJu/authentik/outpost/pkg"
|
||||||
|
"github.com/getsentry/sentry-go"
|
||||||
|
httptransport "github.com/go-openapi/runtime/client"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
func doGlobalSetup(config map[string]interface{}) {
|
||||||
|
log.SetFormatter(&log.JSONFormatter{})
|
||||||
|
switch config[ConfigLogLevel].(string) {
|
||||||
|
case "debug":
|
||||||
|
log.SetLevel(log.DebugLevel)
|
||||||
|
case "info":
|
||||||
|
log.SetLevel(log.InfoLevel)
|
||||||
|
case "warning":
|
||||||
|
log.SetLevel(log.WarnLevel)
|
||||||
|
case "error":
|
||||||
|
log.SetLevel(log.ErrorLevel)
|
||||||
|
default:
|
||||||
|
log.SetLevel(log.DebugLevel)
|
||||||
|
}
|
||||||
|
log.WithField("version", pkg.VERSION).Info("Starting authentik proxy")
|
||||||
|
|
||||||
|
var dsn string
|
||||||
|
if config[ConfigErrorReportingEnabled].(bool) {
|
||||||
|
dsn = "https://a579bb09306d4f8b8d8847c052d3a1d3@sentry.beryju.org/8"
|
||||||
|
log.Debug("Error reporting enabled")
|
||||||
|
}
|
||||||
|
|
||||||
|
err := sentry.Init(sentry.ClientOptions{
|
||||||
|
Dsn: dsn,
|
||||||
|
Environment: config[ConfigErrorReportingEnvironment].(string),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("sentry.Init: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
defer sentry.Flush(2 * time.Second)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getTLSTransport() http.RoundTripper {
|
||||||
|
value, set := os.LookupEnv("AUTHENTIK_INSECURE")
|
||||||
|
if !set {
|
||||||
|
value = "false"
|
||||||
|
}
|
||||||
|
tlsTransport, err := httptransport.TLSTransport(httptransport.TLSClientOptions{
|
||||||
|
InsecureSkipVerify: strings.ToLower(value) == "true",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return tlsTransport
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
package ak
|
||||||
|
|
||||||
|
type Outpost interface {
|
||||||
|
Start() error
|
||||||
|
Refresh() error
|
||||||
|
}
|
|
@ -0,0 +1,48 @@
|
||||||
|
package proxy
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/url"
|
||||||
|
|
||||||
|
"github.com/BeryJu/authentik/outpost/pkg/models"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
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 []*models.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("component", "proxy-bundle").WithField("provider", provider.Name),
|
||||||
|
}
|
||||||
|
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
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package server
|
package proxy
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
@ -11,7 +11,6 @@ import (
|
||||||
|
|
||||||
"github.com/BeryJu/authentik/outpost/pkg/client/crypto"
|
"github.com/BeryJu/authentik/outpost/pkg/client/crypto"
|
||||||
"github.com/BeryJu/authentik/outpost/pkg/models"
|
"github.com/BeryJu/authentik/outpost/pkg/models"
|
||||||
"github.com/BeryJu/authentik/outpost/pkg/proxy"
|
|
||||||
"github.com/jinzhu/copier"
|
"github.com/jinzhu/copier"
|
||||||
"github.com/justinas/alice"
|
"github.com/justinas/alice"
|
||||||
"github.com/oauth2-proxy/oauth2-proxy/pkg/apis/options"
|
"github.com/oauth2-proxy/oauth2-proxy/pkg/apis/options"
|
||||||
|
@ -23,11 +22,13 @@ import (
|
||||||
type providerBundle struct {
|
type providerBundle struct {
|
||||||
http.Handler
|
http.Handler
|
||||||
|
|
||||||
a *APIController
|
s *Server
|
||||||
proxy *proxy.OAuthProxy
|
proxy *OAuthProxy
|
||||||
Host string
|
Host string
|
||||||
|
|
||||||
cert *tls.Certificate
|
cert *tls.Certificate
|
||||||
|
|
||||||
|
log *log.Entry
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pb *providerBundle) prepareOpts(provider *models.ProxyOutpostConfig) *options.Options {
|
func (pb *providerBundle) prepareOpts(provider *models.ProxyOutpostConfig) *options.Options {
|
||||||
|
@ -37,7 +38,7 @@ func (pb *providerBundle) prepareOpts(provider *models.ProxyOutpostConfig) *opti
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
providerOpts := &options.Options{}
|
providerOpts := &options.Options{}
|
||||||
copier.Copy(&providerOpts, &pb.a.commonOpts)
|
copier.Copy(&providerOpts, getCommonOptions())
|
||||||
providerOpts.ClientID = provider.ClientID
|
providerOpts.ClientID = provider.ClientID
|
||||||
providerOpts.ClientSecret = provider.ClientSecret
|
providerOpts.ClientSecret = provider.ClientSecret
|
||||||
|
|
||||||
|
@ -66,22 +67,22 @@ func (pb *providerBundle) prepareOpts(provider *models.ProxyOutpostConfig) *opti
|
||||||
}
|
}
|
||||||
|
|
||||||
if provider.Certificate != nil {
|
if provider.Certificate != nil {
|
||||||
pb.a.logger.WithField("provider", provider.ClientID).Debug("Enabling TLS")
|
pb.log.WithField("provider", provider.ClientID).Debug("Enabling TLS")
|
||||||
cert, err := pb.a.client.Crypto.CryptoCertificatekeypairsRead(&crypto.CryptoCertificatekeypairsReadParams{
|
cert, err := pb.s.ak.Client.Crypto.CryptoCertificatekeypairsRead(&crypto.CryptoCertificatekeypairsReadParams{
|
||||||
Context: context.Background(),
|
Context: context.Background(),
|
||||||
KpUUID: *provider.Certificate,
|
KpUUID: *provider.Certificate,
|
||||||
}, pb.a.auth)
|
}, pb.s.ak.Auth)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
pb.a.logger.WithField("provider", provider.ClientID).WithError(err).Warning("Failed to fetch certificate")
|
pb.log.WithField("provider", provider.ClientID).WithError(err).Warning("Failed to fetch certificate")
|
||||||
return providerOpts
|
return providerOpts
|
||||||
}
|
}
|
||||||
x509cert, err := tls.X509KeyPair([]byte(*cert.Payload.CertificateData), []byte(cert.Payload.KeyData))
|
x509cert, err := tls.X509KeyPair([]byte(*cert.Payload.CertificateData), []byte(cert.Payload.KeyData))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
pb.a.logger.WithField("provider", provider.ClientID).WithError(err).Warning("Failed to parse certificate")
|
pb.log.WithField("provider", provider.ClientID).WithError(err).Warning("Failed to parse certificate")
|
||||||
return providerOpts
|
return providerOpts
|
||||||
}
|
}
|
||||||
pb.cert = &x509cert
|
pb.cert = &x509cert
|
||||||
pb.a.logger.WithField("provider", provider.ClientID).WithField("certificate-key-pair", *cert.Payload.Name).Debug("Loaded certificates")
|
pb.log.WithField("provider", provider.ClientID).WithField("certificate-key-pair", *cert.Payload.Name).Debug("Loaded certificates")
|
||||||
}
|
}
|
||||||
return providerOpts
|
return providerOpts
|
||||||
}
|
}
|
||||||
|
@ -119,7 +120,7 @@ func (pb *providerBundle) Build(provider *models.ProxyOutpostConfig) {
|
||||||
log.Printf("%s", err)
|
log.Printf("%s", err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
oauthproxy, err := proxy.NewOAuthProxy(opts)
|
oauthproxy, err := NewOAuthProxy(opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("ERROR: Failed to initialise OAuth2 Proxy: %v", err)
|
log.Errorf("ERROR: Failed to initialise OAuth2 Proxy: %v", err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
|
@ -0,0 +1,20 @@
|
||||||
|
package proxy
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/oauth2-proxy/oauth2-proxy/pkg/apis/options"
|
||||||
|
)
|
||||||
|
|
||||||
|
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"
|
||||||
|
return commonOpts
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package server
|
package proxy
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
|
@ -95,7 +95,7 @@ type loggingHandler struct {
|
||||||
func LoggingHandler(h http.Handler) http.Handler {
|
func LoggingHandler(h http.Handler) http.Handler {
|
||||||
return loggingHandler{
|
return loggingHandler{
|
||||||
handler: h,
|
handler: h,
|
||||||
logger: log.WithField("component", "http-server"),
|
logger: log.WithField("component", "proxy-http-server"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package server
|
package proxy
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
@ -8,6 +8,7 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/BeryJu/authentik/outpost/pkg/ak"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -17,68 +18,24 @@ type Server struct {
|
||||||
|
|
||||||
stop chan struct{} // channel for waiting shutdown
|
stop chan struct{} // channel for waiting shutdown
|
||||||
logger *log.Entry
|
logger *log.Entry
|
||||||
|
ak *ak.APIController
|
||||||
defaultCert tls.Certificate
|
defaultCert tls.Certificate
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewServer initialise a new HTTP Server
|
// NewServer initialise a new HTTP Server
|
||||||
func NewServer() *Server {
|
func NewServer(ac *ak.APIController) *Server {
|
||||||
defaultCert, err := generateSelfSignedCert()
|
defaultCert, err := ak.GenerateSelfSignedCert()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warning(err)
|
log.Warning(err)
|
||||||
}
|
}
|
||||||
return &Server{
|
return &Server{
|
||||||
Handlers: make(map[string]*providerBundle),
|
Handlers: make(map[string]*providerBundle),
|
||||||
logger: log.WithField("component", "http-server"),
|
logger: log.WithField("component", "proxy-http-server"),
|
||||||
defaultCert: defaultCert,
|
defaultCert: defaultCert,
|
||||||
|
ak: ac,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ServeHTTP constructs a net.Listener and starts handling HTTP requests
|
|
||||||
func (s *Server) ServeHTTP() {
|
|
||||||
listenAddress := "0.0.0.0:4180"
|
|
||||||
listener, err := net.Listen("tcp", listenAddress)
|
|
||||||
if err != nil {
|
|
||||||
s.logger.Fatalf("FATAL: listen (%s) failed - %s", listenAddress, err)
|
|
||||||
}
|
|
||||||
s.logger.Printf("listening on %s", listener.Addr())
|
|
||||||
s.serve(listener)
|
|
||||||
s.logger.Printf("closing %s", listener.Addr())
|
|
||||||
}
|
|
||||||
|
|
||||||
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 := "0.0.0.0: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())
|
|
||||||
|
|
||||||
tlsListener := tls.NewListener(tcpKeepAliveListener{ln.(*net.TCPListener)}, config)
|
|
||||||
s.serve(tlsListener)
|
|
||||||
s.logger.Printf("closing %s", tlsListener.Addr())
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Server) handler(w http.ResponseWriter, r *http.Request) {
|
func (s *Server) handler(w http.ResponseWriter, r *http.Request) {
|
||||||
if r.URL.Path == "/akprox/ping" {
|
if r.URL.Path == "/akprox/ping" {
|
||||||
w.WriteHeader(204)
|
w.WriteHeader(204)
|
|
@ -0,0 +1,68 @@
|
||||||
|
package proxy
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
"net"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ServeHTTP constructs a net.Listener and starts handling HTTP requests
|
||||||
|
func (s *Server) ServeHTTP() {
|
||||||
|
listenAddress := "0.0.0.0:4180"
|
||||||
|
listener, err := net.Listen("tcp", listenAddress)
|
||||||
|
if err != nil {
|
||||||
|
s.logger.Fatalf("FATAL: listen (%s) failed - %s", listenAddress, err)
|
||||||
|
}
|
||||||
|
s.logger.Printf("listening on %s", listener.Addr())
|
||||||
|
s.serve(listener)
|
||||||
|
s.logger.Printf("closing %s", listener.Addr())
|
||||||
|
}
|
||||||
|
|
||||||
|
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 := "0.0.0.0: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())
|
||||||
|
|
||||||
|
tlsListener := tls.NewListener(tcpKeepAliveListener{ln.(*net.TCPListener)}, 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
|
||||||
|
}
|
|
@ -1,225 +0,0 @@
|
||||||
package server
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/sha512"
|
|
||||||
"encoding/hex"
|
|
||||||
"fmt"
|
|
||||||
"math/rand"
|
|
||||||
"net/http"
|
|
||||||
"net/url"
|
|
||||||
"os"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/BeryJu/authentik/outpost/pkg"
|
|
||||||
"github.com/BeryJu/authentik/outpost/pkg/client"
|
|
||||||
"github.com/BeryJu/authentik/outpost/pkg/client/outposts"
|
|
||||||
"github.com/getsentry/sentry-go"
|
|
||||||
"github.com/go-openapi/runtime"
|
|
||||||
"github.com/recws-org/recws"
|
|
||||||
|
|
||||||
httptransport "github.com/go-openapi/runtime/client"
|
|
||||||
"github.com/go-openapi/strfmt"
|
|
||||||
"github.com/oauth2-proxy/oauth2-proxy/pkg/apis/options"
|
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
)
|
|
||||||
|
|
||||||
const ConfigLogLevel = "log_level"
|
|
||||||
const ConfigErrorReportingEnabled = "error_reporting_enabled"
|
|
||||||
const ConfigErrorReportingEnvironment = "error_reporting_environment"
|
|
||||||
|
|
||||||
// APIController main controller which connects to the authentik api via http and ws
|
|
||||||
type APIController struct {
|
|
||||||
client *client.Authentik
|
|
||||||
auth runtime.ClientAuthInfoWriter
|
|
||||||
token string
|
|
||||||
|
|
||||||
server *Server
|
|
||||||
|
|
||||||
commonOpts *options.Options
|
|
||||||
|
|
||||||
lastBundleHash string
|
|
||||||
logger *log.Entry
|
|
||||||
|
|
||||||
reloadOffset time.Duration
|
|
||||||
|
|
||||||
wsConn *recws.RecConn
|
|
||||||
}
|
|
||||||
|
|
||||||
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"
|
|
||||||
return commonOpts
|
|
||||||
}
|
|
||||||
|
|
||||||
func doGlobalSetup(config map[string]interface{}) {
|
|
||||||
log.SetFormatter(&log.JSONFormatter{})
|
|
||||||
switch config[ConfigLogLevel].(string) {
|
|
||||||
case "debug":
|
|
||||||
log.SetLevel(log.DebugLevel)
|
|
||||||
case "info":
|
|
||||||
log.SetLevel(log.InfoLevel)
|
|
||||||
case "warning":
|
|
||||||
log.SetLevel(log.WarnLevel)
|
|
||||||
case "error":
|
|
||||||
log.SetLevel(log.ErrorLevel)
|
|
||||||
default:
|
|
||||||
log.SetLevel(log.DebugLevel)
|
|
||||||
}
|
|
||||||
log.WithField("version", pkg.VERSION).Info("Starting authentik proxy")
|
|
||||||
|
|
||||||
var dsn string
|
|
||||||
if config[ConfigErrorReportingEnabled].(bool) {
|
|
||||||
dsn = "https://a579bb09306d4f8b8d8847c052d3a1d3@sentry.beryju.org/8"
|
|
||||||
log.Debug("Error reporting enabled")
|
|
||||||
}
|
|
||||||
|
|
||||||
err := sentry.Init(sentry.ClientOptions{
|
|
||||||
Dsn: dsn,
|
|
||||||
Environment: config[ConfigErrorReportingEnvironment].(string),
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("sentry.Init: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
defer sentry.Flush(2 * time.Second)
|
|
||||||
}
|
|
||||||
|
|
||||||
func getTLSTransport() http.RoundTripper {
|
|
||||||
value, set := os.LookupEnv("AUTHENTIK_INSECURE")
|
|
||||||
if !set {
|
|
||||||
value = "false"
|
|
||||||
}
|
|
||||||
tlsTransport, err := httptransport.TLSTransport(httptransport.TLSClientOptions{
|
|
||||||
InsecureSkipVerify: strings.ToLower(value) == "true",
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
return tlsTransport
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewAPIController initialise new API Controller instance from URL and API token
|
|
||||||
func NewAPIController(pbURL url.URL, token string) *APIController {
|
|
||||||
transport := httptransport.New(pbURL.Host, client.DefaultBasePath, []string{pbURL.Scheme})
|
|
||||||
transport.Transport = SetUserAgent(getTLSTransport(), fmt.Sprintf("authentik-proxy@%s", pkg.VERSION))
|
|
||||||
|
|
||||||
// create the transport
|
|
||||||
auth := httptransport.BasicAuth("", token)
|
|
||||||
|
|
||||||
// create the API client, with the transport
|
|
||||||
apiClient := client.New(transport, strfmt.Default)
|
|
||||||
|
|
||||||
// Because we don't know the outpost UUID, we simply do a list and pick the first
|
|
||||||
// The service account this token belongs to should only have access to a single outpost
|
|
||||||
outposts, err := apiClient.Outposts.OutpostsOutpostsList(outposts.NewOutpostsOutpostsListParams(), auth)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
outpost := outposts.Payload.Results[0]
|
|
||||||
doGlobalSetup(outpost.Config.(map[string]interface{}))
|
|
||||||
|
|
||||||
ac := &APIController{
|
|
||||||
client: apiClient,
|
|
||||||
auth: auth,
|
|
||||||
token: token,
|
|
||||||
|
|
||||||
logger: log.WithField("component", "api-controller"),
|
|
||||||
commonOpts: getCommonOptions(),
|
|
||||||
server: NewServer(),
|
|
||||||
|
|
||||||
reloadOffset: time.Duration(rand.Intn(10)) * time.Second,
|
|
||||||
|
|
||||||
lastBundleHash: "",
|
|
||||||
}
|
|
||||||
ac.logger.Debugf("HA Reload offset: %s", ac.reloadOffset)
|
|
||||||
ac.initWS(pbURL, outpost.Pk)
|
|
||||||
return ac
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *APIController) bundleProviders() ([]*providerBundle, error) {
|
|
||||||
providers, err := a.client.Outposts.OutpostsProxyList(outposts.NewOutpostsProxyListParams(), a.auth)
|
|
||||||
if err != nil {
|
|
||||||
a.logger.WithError(err).Error("Failed to fetch providers")
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
// Check provider hash to see if anything is changed
|
|
||||||
hasher := sha512.New()
|
|
||||||
bin, _ := providers.Payload.MarshalBinary()
|
|
||||||
hash := hex.EncodeToString(hasher.Sum(bin))
|
|
||||||
if hash == a.lastBundleHash {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
a.lastBundleHash = hash
|
|
||||||
|
|
||||||
bundles := make([]*providerBundle, len(providers.Payload.Results))
|
|
||||||
|
|
||||||
for idx, provider := range providers.Payload.Results {
|
|
||||||
externalHost, err := url.Parse(*provider.ExternalHost)
|
|
||||||
if err != nil {
|
|
||||||
log.WithError(err).Warning("Failed to parse URL, skipping provider")
|
|
||||||
}
|
|
||||||
bundles[idx] = &providerBundle{
|
|
||||||
a: a,
|
|
||||||
Host: externalHost.Host,
|
|
||||||
}
|
|
||||||
bundles[idx].Build(provider)
|
|
||||||
}
|
|
||||||
return bundles, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *APIController) updateHTTPServer(bundles []*providerBundle) {
|
|
||||||
newMap := make(map[string]*providerBundle)
|
|
||||||
for _, bundle := range bundles {
|
|
||||||
newMap[bundle.Host] = bundle
|
|
||||||
}
|
|
||||||
a.logger.Debug("Swapped maps")
|
|
||||||
a.server.Handlers = newMap
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdateIfRequired Updates the HTTP Server config if required, automatically swaps the handlers
|
|
||||||
func (a *APIController) UpdateIfRequired() error {
|
|
||||||
bundles, err := a.bundleProviders()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if bundles == nil {
|
|
||||||
a.logger.Debug("Providers have not changed, not updating")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
a.updateHTTPServer(bundles)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start Starts all handlers, non-blocking
|
|
||||||
func (a *APIController) Start() error {
|
|
||||||
err := a.UpdateIfRequired()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
go func() {
|
|
||||||
a.logger.Debug("Starting HTTP Server...")
|
|
||||||
a.server.ServeHTTP()
|
|
||||||
}()
|
|
||||||
go func() {
|
|
||||||
a.logger.Debug("Starting HTTPs Server...")
|
|
||||||
a.server.ServeHTTPS()
|
|
||||||
}()
|
|
||||||
go func() {
|
|
||||||
a.logger.Debug("Starting WS Handler...")
|
|
||||||
a.startWSHandler()
|
|
||||||
}()
|
|
||||||
go func() {
|
|
||||||
a.logger.Debug("Starting WS Health notifier...")
|
|
||||||
a.startWSHealth()
|
|
||||||
}()
|
|
||||||
return nil
|
|
||||||
}
|
|
Reference in New Issue