mirror of
https://github.com/alibaba/higress.git
synced 2026-02-24 12:40:48 +08:00
159 lines
4.3 KiB
Go
159 lines
4.3 KiB
Go
// Copyright (c) 2022 Alibaba Group Holding Ltd.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package annotations
|
|
|
|
import (
|
|
"errors"
|
|
"sort"
|
|
"strings"
|
|
|
|
corev1 "k8s.io/api/core/v1"
|
|
|
|
"github.com/alibaba/higress/pkg/ingress/kube/util"
|
|
. "github.com/alibaba/higress/pkg/ingress/log"
|
|
)
|
|
|
|
const (
|
|
authType = "auth-type"
|
|
authRealm = "auth-realm"
|
|
authSecretAnn = "auth-secret"
|
|
authSecretTypeAnn = "auth-secret-type"
|
|
|
|
defaultAuthType = "basic"
|
|
authFileKey = "auth"
|
|
)
|
|
|
|
type authSecretType string
|
|
|
|
const (
|
|
authFileAuthSecretType authSecretType = "auth-file"
|
|
authMapAuthSecretType authSecretType = "auth-map"
|
|
)
|
|
|
|
var _ Parser = auth{}
|
|
|
|
type AuthConfig struct {
|
|
AuthType string
|
|
AuthRealm string
|
|
Credentials []string
|
|
AuthSecret util.ClusterNamespacedName
|
|
}
|
|
|
|
type auth struct{}
|
|
|
|
func (a auth) Parse(annotations Annotations, config *Ingress, globalContext *GlobalContext) error {
|
|
if !needAuthConfig(annotations) {
|
|
return nil
|
|
}
|
|
|
|
authConfig := &AuthConfig{
|
|
AuthType: defaultAuthType,
|
|
}
|
|
|
|
// Check auth type
|
|
authType, err := annotations.ParseStringASAP(authType)
|
|
if err != nil {
|
|
IngressLog.Errorf("Parse auth type error %v within ingress %/%s", err, config.Namespace, config.Name)
|
|
return nil
|
|
}
|
|
if authType != defaultAuthType {
|
|
IngressLog.Errorf("Auth type %s within ingress %/%s is not supported yet.", authType, config.Namespace, config.Name)
|
|
return nil
|
|
}
|
|
|
|
secretName, _ := annotations.ParseStringASAP(authSecretAnn)
|
|
namespaced := util.SplitNamespacedName(secretName)
|
|
if namespaced.Name == "" {
|
|
IngressLog.Errorf("Auth secret name within ingress %s/%s is invalid", config.Namespace, config.Name)
|
|
return nil
|
|
}
|
|
if namespaced.Namespace == "" {
|
|
namespaced.Namespace = config.Namespace
|
|
}
|
|
|
|
configKey := util.ClusterNamespacedName{
|
|
NamespacedName: namespaced,
|
|
ClusterId: config.ClusterId,
|
|
}
|
|
authConfig.AuthSecret = configKey
|
|
|
|
// Subscribe secret
|
|
globalContext.WatchedSecrets.Insert(configKey.String())
|
|
|
|
secretType := authFileAuthSecretType
|
|
if rawSecretType, err := annotations.ParseStringASAP(authSecretTypeAnn); err == nil {
|
|
resultAuthSecretType := authSecretType(rawSecretType)
|
|
if resultAuthSecretType == authFileAuthSecretType || resultAuthSecretType == authMapAuthSecretType {
|
|
secretType = resultAuthSecretType
|
|
}
|
|
}
|
|
|
|
authConfig.AuthRealm, _ = annotations.ParseStringASAP(authRealm)
|
|
|
|
// Process credentials.
|
|
secretLister, exist := globalContext.ClusterSecretLister[config.ClusterId]
|
|
if !exist {
|
|
IngressLog.Errorf("secret lister of cluster %s doesn't exist", config.ClusterId)
|
|
return nil
|
|
}
|
|
authSecret, err := secretLister.Secrets(namespaced.Namespace).Get(namespaced.Name)
|
|
if err != nil {
|
|
IngressLog.Errorf("Secret %s within ingress %s/%s is not found",
|
|
namespaced.String(), config.Namespace, config.Name)
|
|
return nil
|
|
}
|
|
credentials, err := convertCredentials(secretType, authSecret)
|
|
if err != nil {
|
|
IngressLog.Errorf("Parse auth secret fail, err %v", err)
|
|
return nil
|
|
}
|
|
authConfig.Credentials = credentials
|
|
|
|
config.Auth = authConfig
|
|
return nil
|
|
}
|
|
|
|
func convertCredentials(secretType authSecretType, secret *corev1.Secret) ([]string, error) {
|
|
var result []string
|
|
switch secretType {
|
|
case authFileAuthSecretType:
|
|
users, exist := secret.Data[authFileKey]
|
|
if !exist {
|
|
return nil, errors.New("the auth file type must has auth key in secret data")
|
|
}
|
|
userList := strings.Split(string(users), "\n")
|
|
for _, item := range userList {
|
|
if !strings.Contains(item, ":") {
|
|
continue
|
|
}
|
|
result = append(result, item)
|
|
}
|
|
case authMapAuthSecretType:
|
|
for name, password := range secret.Data {
|
|
result = append(result, name+":"+string(password))
|
|
}
|
|
}
|
|
sort.SliceStable(result, func(i, j int) bool {
|
|
return result[i] < result[j]
|
|
})
|
|
|
|
return result, nil
|
|
}
|
|
|
|
func needAuthConfig(annotations Annotations) bool {
|
|
return annotations.HasASAP(authType) &&
|
|
annotations.HasASAP(authSecretAnn)
|
|
}
|