Files
authentik/internal/web/brand_tls/brand_tls.go
Jens L. 65517f3b7f enterprise/stages: Add MTLS stage (#14296)
* prepare client auth with inbuilt server

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* introduce better IPC auth

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* init

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* start stage

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* only allow trusted proxies to set MTLS headers

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* more stage progress

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* dont fail if ipc_key doesn't exist

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* actually install app

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* fix

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* add some tests

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* update API

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* fix unquote

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* fix int serial number not jsonable

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* init ui

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* add UI

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* unrelated: fix git pull in makefile

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* fix parse helper

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* add test for outpost

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* more tests and improvements

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* improve labels

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* add support for multiple CAs on brand

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* add support for multiple CAs to MTLS stage

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* dont log ipcuser secret views

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* fix go mod

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2025-05-19 22:48:17 +02:00

118 lines
2.5 KiB
Go

package brand_tls
import (
"context"
"crypto/tls"
"crypto/x509"
"strings"
"time"
log "github.com/sirupsen/logrus"
"goauthentik.io/api/v3"
"goauthentik.io/internal/crypto"
"goauthentik.io/internal/outpost/ak"
)
type Watcher struct {
client *api.APIClient
log *log.Entry
cs *ak.CryptoStore
fallback *tls.Certificate
brands []api.Brand
}
func NewWatcher(client *api.APIClient) *Watcher {
cs := ak.NewCryptoStore(client.CryptoApi)
l := log.WithField("logger", "authentik.router.brand_tls")
cert, err := crypto.GenerateSelfSignedCert()
if err != nil {
l.WithError(err).Error("failed to generate default cert")
}
return &Watcher{
client: client,
log: l,
cs: cs,
fallback: &cert,
}
}
func (w *Watcher) Start() {
ticker := time.NewTicker(time.Minute * 3)
w.log.Info("Starting Brand TLS Checker")
for ; true; <-ticker.C {
w.Check()
}
}
func (w *Watcher) Check() {
w.log.Info("updating brand certificates")
brands, err := ak.Paginator(w.client.CoreApi.CoreBrandsList(context.Background()), ak.PaginatorOptions{
PageSize: 100,
Logger: w.log,
})
if err != nil {
w.log.WithError(err).Warning("failed to get brands")
return
}
for _, b := range brands {
kp := b.GetWebCertificate()
if kp != "" {
err := w.cs.AddKeypair(kp)
if err != nil {
w.log.WithError(err).WithField("kp", kp).Warning("failed to add web certificate")
}
}
for _, crt := range b.GetClientCertificates() {
if crt != "" {
err := w.cs.AddKeypair(crt)
if err != nil {
w.log.WithError(err).WithField("kp", kp).Warning("failed to add client certificate")
}
}
}
}
w.brands = brands
}
type CertificateConfig struct {
Web *tls.Certificate
Client *x509.CertPool
}
func (w *Watcher) GetCertificate(ch *tls.ClientHelloInfo) *CertificateConfig {
var bestSelection *api.Brand
config := CertificateConfig{
Web: w.fallback,
}
for _, t := range w.brands {
if !t.WebCertificate.IsSet() && len(t.GetClientCertificates()) < 1 {
continue
}
if *t.Default {
bestSelection = &t
}
if strings.HasSuffix(ch.ServerName, t.Domain) {
bestSelection = &t
}
}
if bestSelection == nil {
return &config
}
if bestSelection.GetWebCertificate() != "" {
if cert := w.cs.Get(bestSelection.GetWebCertificate()); cert != nil {
config.Web = cert
}
}
if len(bestSelection.GetClientCertificates()) > 0 {
config.Client = x509.NewCertPool()
for _, kp := range bestSelection.GetClientCertificates() {
if cert := w.cs.Get(kp); cert != nil {
config.Client.AddCert(cert.Leaf)
}
}
}
return &config
}