github.com/imran-kn/cilium-fork@v1.6.9/pkg/envoy/accesslog_server.go (about)

     1  // Copyright 2017, 2018 Authors of Cilium
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package envoy
    16  
    17  import (
    18  	"fmt"
    19  	"net"
    20  	"os"
    21  	"path/filepath"
    22  	"strings"
    23  	"syscall"
    24  	"time"
    25  
    26  	"github.com/cilium/cilium/pkg/flowdebug"
    27  	"github.com/cilium/cilium/pkg/proxy/accesslog"
    28  	"github.com/cilium/cilium/pkg/proxy/logger"
    29  
    30  	"github.com/cilium/proxy/go/cilium/api"
    31  	"github.com/golang/protobuf/proto"
    32  	"github.com/sirupsen/logrus"
    33  )
    34  
    35  func getAccessLogPath(stateDir string) string {
    36  	return filepath.Join(stateDir, "access_log.sock")
    37  }
    38  
    39  type accessLogServer struct {
    40  	xdsServer            *XDSServer
    41  	endpointInfoRegistry logger.EndpointInfoRegistry
    42  }
    43  
    44  // StartAccessLogServer starts the access log server.
    45  func StartAccessLogServer(stateDir string, xdsServer *XDSServer, endpointInfoRegistry logger.EndpointInfoRegistry) {
    46  	accessLogPath := getAccessLogPath(stateDir)
    47  
    48  	// Create the access log listener
    49  	os.Remove(accessLogPath) // Remove/Unlink the old unix domain socket, if any.
    50  	accessLogListener, err := net.ListenUnix("unixpacket", &net.UnixAddr{Name: accessLogPath, Net: "unixpacket"})
    51  	if err != nil {
    52  		log.WithError(err).Fatalf("Envoy: Failed to open access log listen socket at %s", accessLogPath)
    53  	}
    54  	accessLogListener.SetUnlinkOnClose(true)
    55  
    56  	// Make the socket accessible by non-root Envoy proxies, e.g. running in
    57  	// sidecar containers.
    58  	if err = os.Chmod(accessLogPath, 0777); err != nil {
    59  		log.WithError(err).Fatalf("Envoy: Failed to change mode of access log listen socket at %s", accessLogPath)
    60  	}
    61  
    62  	server := accessLogServer{
    63  		xdsServer:            xdsServer,
    64  		endpointInfoRegistry: endpointInfoRegistry,
    65  	}
    66  
    67  	go func() {
    68  		for {
    69  			// Each Envoy listener opens a new connection over the Unix domain socket.
    70  			// Multiple worker threads serving the listener share that same connection
    71  			uc, err := accessLogListener.AcceptUnix()
    72  			if err != nil {
    73  				// These errors are expected when we are closing down
    74  				if strings.Contains(err.Error(), "closed network connection") ||
    75  					strings.Contains(err.Error(), "invalid argument") {
    76  					break
    77  				}
    78  				log.WithError(err).Warn("Envoy: Failed to accept access log connection")
    79  				continue
    80  			}
    81  			log.Info("Envoy: Accepted access log connection")
    82  
    83  			// Serve this access log socket in a goroutine, so we can serve multiple
    84  			// connections concurrently.
    85  			go server.accessLogger(uc)
    86  		}
    87  	}()
    88  }
    89  
    90  func (s *accessLogServer) accessLogger(conn *net.UnixConn) {
    91  	defer func() {
    92  		log.Info("Envoy: Closing access log connection")
    93  		conn.Close()
    94  	}()
    95  
    96  	buf := make([]byte, 4096)
    97  	for {
    98  		n, _, flags, _, err := conn.ReadMsgUnix(buf, nil)
    99  		if err != nil {
   100  			if !isEOF(err) {
   101  				log.WithError(err).Error("Envoy: Error while reading from access log connection")
   102  			}
   103  			break
   104  		}
   105  		if flags&syscall.MSG_TRUNC != 0 {
   106  			log.Warning("Envoy: Discarded truncated access log message")
   107  			continue
   108  		}
   109  		pblog := cilium.LogEntry{} // TODO: Support Kafka.
   110  		err = proto.Unmarshal(buf[:n], &pblog)
   111  		if err != nil {
   112  			log.WithError(err).Warning("Envoy: Discarded invalid access log message")
   113  			continue
   114  		}
   115  
   116  		flowdebug.Log(log.WithFields(logrus.Fields{}),
   117  			fmt.Sprintf("%s: Access log message: %s", pblog.PolicyName, pblog.String()))
   118  
   119  		// Correlate the log entry's network policy name with a local endpoint info source.
   120  		localEndpoint := s.xdsServer.getLocalEndpoint(pblog.PolicyName)
   121  		if localEndpoint == nil {
   122  			log.Warnf("Envoy: Discarded access log message for non-existent network policy %s",
   123  				pblog.PolicyName)
   124  			continue
   125  		}
   126  
   127  		s.logRecord(localEndpoint, &pblog)
   128  	}
   129  }
   130  
   131  func (s *accessLogServer) logRecord(localEndpoint logger.EndpointUpdater, pblog *cilium.LogEntry) {
   132  	// TODO: Support Kafka.
   133  
   134  	var l7tags logger.LogTag
   135  	if http := pblog.GetHttp(); http != nil {
   136  		l7tags = logger.LogTags.HTTP(&accesslog.LogRecordHTTP{
   137  			Method:   http.Method,
   138  			Code:     int(http.Status),
   139  			URL:      ParseURL(http.Scheme, http.Host, http.Path),
   140  			Protocol: GetProtocol(http.HttpProtocol),
   141  			Headers:  GetNetHttpHeaders(http.Headers),
   142  		})
   143  	} else if l7 := pblog.GetGenericL7(); l7 != nil {
   144  		l7tags = logger.LogTags.L7(&accesslog.LogRecordL7{
   145  			Proto:  l7.GetProto(),
   146  			Fields: l7.GetFields(),
   147  		})
   148  	} else {
   149  		// Default to the deprecated HTTP log format
   150  		l7tags = logger.LogTags.HTTP(&accesslog.LogRecordHTTP{
   151  			Method:   pblog.Method,
   152  			Code:     int(pblog.Status),
   153  			URL:      ParseURL(pblog.Scheme, pblog.Host, pblog.Path),
   154  			Protocol: GetProtocol(pblog.HttpProtocol),
   155  			Headers:  GetNetHttpHeaders(pblog.Headers),
   156  		})
   157  	}
   158  
   159  	r := logger.NewLogRecord(s.endpointInfoRegistry, localEndpoint, GetFlowType(pblog), pblog.IsIngress,
   160  		logger.LogTags.Timestamp(time.Unix(int64(pblog.Timestamp/1000000000), int64(pblog.Timestamp%1000000000))),
   161  		logger.LogTags.Verdict(GetVerdict(pblog), pblog.CiliumRuleRef),
   162  		logger.LogTags.Addressing(logger.AddressingInfo{
   163  			SrcIPPort:   pblog.SourceAddress,
   164  			DstIPPort:   pblog.DestinationAddress,
   165  			SrcIdentity: pblog.SourceSecurityId,
   166  		}), l7tags)
   167  
   168  	r.Log()
   169  
   170  	// Update stats for the endpoint.
   171  	ingress := r.ObservationPoint == accesslog.Ingress
   172  	request := r.Type == accesslog.TypeRequest
   173  	localEndpoint.UpdateProxyStatistics("TCP", r.DestinationEndpoint.Port, ingress, request, r.Verdict)
   174  }