mirror of
https://github.com/tinyauthapp/tinyauth.git
synced 2026-09-23 11:13:32 +08:00
feat: load basic auth password from secret
Co-Authored-By: Codex <codex@openai.com>
This commit is contained in:
@@ -70,6 +70,7 @@ require (
|
||||
github.com/docker/go-connections v0.6.0 // indirect
|
||||
github.com/docker/go-units v0.5.0 // indirect
|
||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||
github.com/emicklei/go-restful/v3 v3.13.0 // indirect
|
||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||
github.com/fxamacker/cbor/v2 v2.9.1 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.12 // indirect
|
||||
@@ -77,6 +78,20 @@ require (
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.8 // indirect
|
||||
github.com/go-logr/logr v1.4.4 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/go-openapi/jsonpointer v1.0.0 // indirect
|
||||
github.com/go-openapi/jsonreference v1.0.0 // indirect
|
||||
github.com/go-openapi/swag v0.27.1 // indirect
|
||||
github.com/go-openapi/swag/cmdutils v0.27.1 // indirect
|
||||
github.com/go-openapi/swag/conv v0.27.1 // indirect
|
||||
github.com/go-openapi/swag/fileutils v0.27.1 // indirect
|
||||
github.com/go-openapi/swag/jsonutils v0.27.1 // indirect
|
||||
github.com/go-openapi/swag/loading v0.27.1 // indirect
|
||||
github.com/go-openapi/swag/mangling v0.27.1 // indirect
|
||||
github.com/go-openapi/swag/netutils v0.27.1 // indirect
|
||||
github.com/go-openapi/swag/pools v0.27.1 // indirect
|
||||
github.com/go-openapi/swag/stringutils v0.27.1 // indirect
|
||||
github.com/go-openapi/swag/typeutils v0.27.1 // indirect
|
||||
github.com/go-openapi/swag/yamlutils v0.27.1 // indirect
|
||||
github.com/go-playground/locales v0.14.1 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||
github.com/go-playground/validator/v10 v10.30.1 // indirect
|
||||
@@ -142,6 +157,7 @@ require (
|
||||
golang.org/x/text v0.41.0 // indirect
|
||||
golang.org/x/time v0.15.0 // indirect
|
||||
google.golang.org/protobuf v1.36.12-0.20260120151049-f2248ac996af // indirect
|
||||
gopkg.in/evanphx/json-patch.v4 v4.13.0 // indirect
|
||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||
gotest.tools/v3 v3.5.2 // indirect
|
||||
k8s.io/klog/v2 v2.140.0 // indirect
|
||||
|
||||
@@ -138,6 +138,8 @@ github.com/go-openapi/swag/fileutils v0.27.1 h1:QQqBSoi5mW4XpU85nS0mLcA+zAE6vLzr
|
||||
github.com/go-openapi/swag/fileutils v0.27.1/go.mod h1:VvJFZLTZS0AI854gEQz5tk7dBESdLjiNUMSZ/th2ry8=
|
||||
github.com/go-openapi/swag/jsonutils v0.27.1 h1:SVgK3i4USzCU5mibOOS/l4ea2h9UQXy7J7RNLTjuXjU=
|
||||
github.com/go-openapi/swag/jsonutils v0.27.1/go.mod h1:tdlEpZqdcQ17uj6J4YdK9vd8It5qWMwjWXOs0tjpRlk=
|
||||
github.com/go-openapi/swag/jsonutils/fixtures_test v0.27.1 h1:mJu3COL9WEaZVp/Kf2PRMi7tPszPEJfSr/OO75ynCs8=
|
||||
github.com/go-openapi/swag/jsonutils/fixtures_test v0.27.1/go.mod h1:mofwUWx70wvskwESqRJ//k/9kURmCgyJl5m5Ppoh5kY=
|
||||
github.com/go-openapi/swag/loading v0.27.1 h1:/DxUgDXKbBX4bcn7r9uEXfJyzN5XpiJmZplzQTjrRCY=
|
||||
github.com/go-openapi/swag/loading v0.27.1/go.mod h1:jvGh3iA2+zyUUycB5fgJWzeHnhrpvGnJJM0RVE9ZShE=
|
||||
github.com/go-openapi/swag/mangling v0.27.1 h1:yC9D0HyUE8gbP+BfmGx9+AA89ikwZTMjESK3OnnoaqA=
|
||||
@@ -152,6 +154,10 @@ github.com/go-openapi/swag/typeutils v0.27.1 h1:KSTdFlfnse4r6dP9IrEnwMldjE+zs71U
|
||||
github.com/go-openapi/swag/typeutils v0.27.1/go.mod h1:Srm0xFNRZ1Y+vCxJclo5qzx8aj+1pAKda/YfFPrG0dQ=
|
||||
github.com/go-openapi/swag/yamlutils v0.27.1 h1:ftxv6xvXb1E3zohUc+okZ9nSqNb9StQX/FXnKZ98sQA=
|
||||
github.com/go-openapi/swag/yamlutils v0.27.1/go.mod h1:bnxFIB1qewGRiZHypXGZ3fNgf13/0HfRgnS/iZBDrOo=
|
||||
github.com/go-openapi/testify/enable/yaml/v2 v2.6.0 h1:gGHwAJ0R/5jU8BEGDbfRNR3hL68dAVi84WuOApp29B0=
|
||||
github.com/go-openapi/testify/enable/yaml/v2 v2.6.0/go.mod h1:tY+St1SGq4NFl0QIqdTY4aEdbChAHxhyB77XQi9iJCo=
|
||||
github.com/go-openapi/testify/v2 v2.6.0 h1:5PKH2HE7YJ/LuRPQGvSxBRlFXNQhSetBLlGAgUEu3ug=
|
||||
github.com/go-openapi/testify/v2 v2.6.0/go.mod h1:SgsVHtfooshd0tublTtJ50FPKhujf47YRqauXXOUxfw=
|
||||
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
|
||||
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
||||
|
||||
@@ -1,22 +1,29 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/tinyauthapp/tinyauth/internal/model"
|
||||
"github.com/tinyauthapp/tinyauth/internal/utils/logger"
|
||||
"github.com/tinyauthapp/tinyauth/pkg/apis/tinyauth/v1alpha1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
)
|
||||
|
||||
type KubernetesCRDInput struct {
|
||||
Log *logger.Logger
|
||||
Log *logger.Logger
|
||||
Client kubernetes.Interface
|
||||
}
|
||||
|
||||
type KubernetesCRDExtractor struct {
|
||||
log *logger.Logger
|
||||
log *logger.Logger
|
||||
client kubernetes.Interface
|
||||
}
|
||||
|
||||
func NewKubernetesCRDExtractor(i KubernetesCRDInput) *KubernetesCRDExtractor {
|
||||
return &KubernetesCRDExtractor{
|
||||
log: i.Log,
|
||||
log: i.Log,
|
||||
client: i.Client,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,11 +55,29 @@ func (k *KubernetesCRDExtractor) Extract(app *v1alpha1.Application) ExtractionRe
|
||||
}
|
||||
}
|
||||
|
||||
// Convert the CRD to the internal representation.
|
||||
internalApp := app.Spec.ToInternalApp()
|
||||
passwordRef := app.Spec.Response.BasicAuth.PasswordSecretRef
|
||||
if passwordRef != nil {
|
||||
secret, err := k.client.CoreV1().Secrets(meta.Namespace).Get(context.Background(), passwordRef.Name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
k.log.App.Warn().Err(err).Str("namespace", meta.Namespace).Str("name", meta.Name).Str("secret", passwordRef.Name).Str("key", passwordRef.Key).Msg("Failed to read basic auth password Secret, skipping")
|
||||
return ExtractionResult{Meta: meta}
|
||||
}
|
||||
|
||||
password, ok := secret.Data[passwordRef.Key]
|
||||
if !ok {
|
||||
k.log.App.Warn().Str("namespace", meta.Namespace).Str("name", meta.Name).Str("secret", passwordRef.Name).Str("key", passwordRef.Key).Msg("Basic auth password Secret key does not exist, skipping")
|
||||
return ExtractionResult{Meta: meta}
|
||||
}
|
||||
|
||||
internalApp.Response.BasicAuth.Password = string(password)
|
||||
}
|
||||
|
||||
return ExtractionResult{
|
||||
Meta: meta,
|
||||
Apps: map[string]model.App{
|
||||
// Convert the CRD to the internal representation
|
||||
meta.Name: app.Spec.ToInternalApp(),
|
||||
meta.Name: internalApp,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ import (
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/watch"
|
||||
"k8s.io/client-go/dynamic"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/rest"
|
||||
)
|
||||
|
||||
@@ -114,10 +115,11 @@ func (ti *typedItem) fromUnstructured(typ ResourceType, obj *unstructured.Unstru
|
||||
type KubernetesService struct {
|
||||
log *logger.Logger
|
||||
|
||||
apps map[ResourceMeta]map[string]model.App
|
||||
client dynamic.Interface
|
||||
mu sync.RWMutex
|
||||
connected bool
|
||||
apps map[ResourceMeta]map[string]model.App
|
||||
client dynamic.Interface
|
||||
typedClient kubernetes.Interface
|
||||
mu sync.RWMutex
|
||||
connected bool
|
||||
}
|
||||
|
||||
type KubernetesServiceInput struct {
|
||||
@@ -138,11 +140,16 @@ func NewKubernetesService(i KubernetesServiceInput) (*KubernetesService, error)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create kubernetes client: %w", err)
|
||||
}
|
||||
typedClient, err := kubernetes.NewForConfig(cfg)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create typed kubernetes client: %w", err)
|
||||
}
|
||||
|
||||
service := &KubernetesService{
|
||||
log: i.Log,
|
||||
client: client,
|
||||
apps: make(map[ResourceMeta]map[string]model.App),
|
||||
log: i.Log,
|
||||
client: client,
|
||||
typedClient: typedClient,
|
||||
apps: make(map[ResourceMeta]map[string]model.App),
|
||||
}
|
||||
|
||||
watchedGVRs := make(map[string]bool)
|
||||
@@ -226,7 +233,8 @@ func (k *KubernetesService) watchedItemChange(res watchedResource, typedItem *ty
|
||||
return
|
||||
}
|
||||
extractor := NewKubernetesCRDExtractor(KubernetesCRDInput{
|
||||
Log: k.log,
|
||||
Log: k.log,
|
||||
Client: k.typedClient,
|
||||
})
|
||||
result = extractor.Extract(typedItem.crd)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
package v1alpha1
|
||||
|
||||
import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
import (
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
//go:generate controller-gen object paths=$GOFILE
|
||||
|
||||
@@ -81,9 +84,8 @@ type AppResponse struct {
|
||||
|
||||
// AppBasicAuth specifies basic auth credentials
|
||||
type AppBasicAuth struct {
|
||||
Username string `json:"username,omitempty"`
|
||||
Password string `json:"password,omitempty"`
|
||||
PasswordFile string `json:"passwordFile,omitempty"`
|
||||
Username string `json:"username,omitempty"`
|
||||
PasswordSecretRef *corev1.SecretKeySelector `json:"passwordSecretRef,omitempty"`
|
||||
}
|
||||
|
||||
// +k8s:deepcopy-gen=true
|
||||
|
||||
@@ -48,11 +48,8 @@ spec:
|
||||
properties:
|
||||
domain:
|
||||
type: string
|
||||
name:
|
||||
type: string
|
||||
required:
|
||||
- domain
|
||||
- name
|
||||
type: object
|
||||
ip:
|
||||
description: AppIP specifies IP access control rules
|
||||
@@ -99,10 +96,30 @@ spec:
|
||||
basicAuth:
|
||||
description: AppBasicAuth specifies basic auth credentials
|
||||
properties:
|
||||
password:
|
||||
type: string
|
||||
passwordFile:
|
||||
type: string
|
||||
passwordSecretRef:
|
||||
description: SecretKeySelector selects a key of a Secret.
|
||||
properties:
|
||||
key:
|
||||
description: The key of the secret to select from. Must
|
||||
be a valid secret key.
|
||||
type: string
|
||||
name:
|
||||
default: ""
|
||||
description: |-
|
||||
Name of the referent.
|
||||
This field is effectively required, but due to backwards compatibility is
|
||||
allowed to be empty. Instances of this type with an empty value here are
|
||||
almost certainly wrong.
|
||||
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
|
||||
type: string
|
||||
optional:
|
||||
description: Specify whether the Secret or its key must
|
||||
be defined
|
||||
type: boolean
|
||||
required:
|
||||
- key
|
||||
type: object
|
||||
x-kubernetes-map-type: atomic
|
||||
username:
|
||||
type: string
|
||||
type: object
|
||||
|
||||
@@ -26,9 +26,7 @@ func (s *ApplicationSpec) ToInternalApp() model.App {
|
||||
Response: model.AppResponse{
|
||||
Headers: s.Response.Headers,
|
||||
BasicAuth: model.AppBasicAuth{
|
||||
Username: s.Response.BasicAuth.Username,
|
||||
Password: s.Response.BasicAuth.Password,
|
||||
PasswordFile: s.Response.BasicAuth.PasswordFile,
|
||||
Username: s.Response.BasicAuth.Username,
|
||||
},
|
||||
},
|
||||
Path: model.AppPath{
|
||||
|
||||
@@ -5,12 +5,18 @@
|
||||
package v1alpha1
|
||||
|
||||
import (
|
||||
"k8s.io/api/core/v1"
|
||||
runtime "k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *AppBasicAuth) DeepCopyInto(out *AppBasicAuth) {
|
||||
*out = *in
|
||||
if in.PasswordSecretRef != nil {
|
||||
in, out := &in.PasswordSecretRef, &out.PasswordSecretRef
|
||||
*out = new(v1.SecretKeySelector)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AppBasicAuth.
|
||||
@@ -121,7 +127,7 @@ func (in *AppResponse) DeepCopyInto(out *AppResponse) {
|
||||
*out = make([]string, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
out.BasicAuth = in.BasicAuth
|
||||
in.BasicAuth.DeepCopyInto(&out.BasicAuth)
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AppResponse.
|
||||
|
||||
Reference in New Issue
Block a user