blob: 6fbb37d61c1e668d4272ce1a0fca08a5cbba56ba [file] [log] [blame]
// Licensed to the Apache Software Foundation (ASF) under one or more
// contributor license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright ownership.
// The ASF licenses this file to You 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 endpoint
import (
"context"
"encoding/json"
"fmt"
"net"
"strings"
"github.com/apache/dubbo-kubernetes/pkg/core/client/cert"
dubbo_cp "github.com/apache/dubbo-kubernetes/pkg/config/app/dubbo-cp"
"github.com/apache/dubbo-kubernetes/pkg/core/cert/provider"
"github.com/apache/dubbo-kubernetes/pkg/core/endpoint"
"github.com/apache/dubbo-kubernetes/pkg/core/jwt"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/metadata"
"google.golang.org/grpc/peer"
)
func ExactEndpoint(c context.Context, certStorage *provider.CertStorage, options *dubbo_cp.Config, certClient cert.Client) (*endpoint.Endpoint, error) {
if c == nil {
return nil, fmt.Errorf("context is nil")
}
p, ok := peer.FromContext(c)
if !ok {
return nil, fmt.Errorf("failed to get peer from context")
}
endpoints, endpointErr := tryFromHeader(c, certStorage, options, certClient)
if endpointErr == nil {
return endpoints, nil
}
endpoints, connectionErr := tryFromConnection(p)
if connectionErr == nil {
return endpoints, nil
}
if !options.Security.IsTrustAnyone && connectionErr != nil {
return nil, fmt.Errorf("failed to get endpoint from header: %s. Failed to get endpoint from storage: %s. RemoteAddr: %s",
endpointErr.Error(), connectionErr.Error(), p.Addr.String())
}
host, _, err := net.SplitHostPort(p.Addr.String())
if err != nil {
return nil, err
}
return &endpoint.Endpoint{
ID: p.Addr.String(),
Ips: []string{host},
}, nil
}
func tryFromHeader(c context.Context, certStorage *provider.CertStorage, options *dubbo_cp.Config, kubeClient cert.Client) (*endpoint.Endpoint, error) {
// TODO refactor as coreos/go-oidc
authorization := metadata.ValueFromIncomingContext(c, "authorization")
if len(authorization) != 1 {
return nil, fmt.Errorf("failed to get Authorization header from context")
}
if !strings.HasPrefix(authorization[0], "Bearer ") {
return nil, fmt.Errorf("failed to get Authorization header from context")
}
token := strings.ReplaceAll(authorization[0], "Bearer ", "")
authorizationTypes := metadata.ValueFromIncomingContext(c, "authorization-type")
authorizationType := "kubernetes"
if len(authorizationTypes) == 1 {
authorizationType = authorizationTypes[0]
}
if authorizationType == "dubbo-jwt" {
for _, c := range certStorage.GetTrustedCerts() {
claims, err := jwt.Verify(&c.PrivateKey.PublicKey, token)
if err != nil {
continue
}
endpoint := &endpoint.Endpoint{SpiffeID: claims.Subject}
err = json.Unmarshal([]byte(claims.Extensions), endpoint)
if err != nil {
continue
}
return endpoint, nil
}
return nil, fmt.Errorf("failed to verify Authorization header from dubbo-jwt")
}
if options.KubeConfig.IsKubernetesConnected && options.Security.EnableOIDCCheck {
endpoint, ok := kubeClient.VerifyServiceAccount(token, authorizationType)
if !ok {
return nil, fmt.Errorf("failed to verify Authorization header from kubernetes")
}
return endpoint, nil
}
return nil, fmt.Errorf("failed to verify Authorization header")
}
func tryFromConnection(p *peer.Peer) (*endpoint.Endpoint, error) {
if p.AuthInfo != nil && p.AuthInfo.AuthType() == "tls" {
tlsInfo, ok := p.AuthInfo.(credentials.TLSInfo)
if !ok {
return nil, fmt.Errorf("failed to get TLSInfo from peer")
}
host, _, err := net.SplitHostPort(p.Addr.String())
if err != nil {
return nil, err
}
if tlsInfo.SPIFFEID == nil {
return nil, fmt.Errorf("failed to get SPIFFE ID from peer")
}
return &endpoint.Endpoint{
ID: p.Addr.String(),
SpiffeID: tlsInfo.SPIFFEID.String(),
Ips: []string{host},
}, nil
}
return nil, fmt.Errorf("failed to get TLSInfo from peer")
}