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 }