github.com/ruphin/docker@v1.10.1/daemon/logger/syslog/syslog.go (about) 1 // +build linux 2 3 // Package syslog provides the logdriver for forwarding server logs to syslog endpoints. 4 package syslog 5 6 import ( 7 "crypto/tls" 8 "errors" 9 "fmt" 10 "net" 11 "net/url" 12 "os" 13 "path" 14 "strconv" 15 "strings" 16 17 syslog "github.com/RackSec/srslog" 18 19 "github.com/Sirupsen/logrus" 20 "github.com/docker/docker/daemon/logger" 21 "github.com/docker/docker/daemon/logger/loggerutils" 22 "github.com/docker/docker/pkg/urlutil" 23 "github.com/docker/go-connections/tlsconfig" 24 ) 25 26 const ( 27 name = "syslog" 28 secureProto = "tcp+tls" 29 ) 30 31 var facilities = map[string]syslog.Priority{ 32 "kern": syslog.LOG_KERN, 33 "user": syslog.LOG_USER, 34 "mail": syslog.LOG_MAIL, 35 "daemon": syslog.LOG_DAEMON, 36 "auth": syslog.LOG_AUTH, 37 "syslog": syslog.LOG_SYSLOG, 38 "lpr": syslog.LOG_LPR, 39 "news": syslog.LOG_NEWS, 40 "uucp": syslog.LOG_UUCP, 41 "cron": syslog.LOG_CRON, 42 "authpriv": syslog.LOG_AUTHPRIV, 43 "ftp": syslog.LOG_FTP, 44 "local0": syslog.LOG_LOCAL0, 45 "local1": syslog.LOG_LOCAL1, 46 "local2": syslog.LOG_LOCAL2, 47 "local3": syslog.LOG_LOCAL3, 48 "local4": syslog.LOG_LOCAL4, 49 "local5": syslog.LOG_LOCAL5, 50 "local6": syslog.LOG_LOCAL6, 51 "local7": syslog.LOG_LOCAL7, 52 } 53 54 type syslogger struct { 55 writer *syslog.Writer 56 } 57 58 func init() { 59 if err := logger.RegisterLogDriver(name, New); err != nil { 60 logrus.Fatal(err) 61 } 62 if err := logger.RegisterLogOptValidator(name, ValidateLogOpt); err != nil { 63 logrus.Fatal(err) 64 } 65 } 66 67 // New creates a syslog logger using the configuration passed in on 68 // the context. Supported context configuration variables are 69 // syslog-address, syslog-facility, & syslog-tag. 70 func New(ctx logger.Context) (logger.Logger, error) { 71 tag, err := loggerutils.ParseLogTag(ctx, "{{.ID}}") 72 if err != nil { 73 return nil, err 74 } 75 76 proto, address, err := parseAddress(ctx.Config["syslog-address"]) 77 if err != nil { 78 return nil, err 79 } 80 81 facility, err := parseFacility(ctx.Config["syslog-facility"]) 82 if err != nil { 83 return nil, err 84 } 85 86 logTag := path.Base(os.Args[0]) + "/" + tag 87 88 var log *syslog.Writer 89 if proto == secureProto { 90 tlsConfig, tlsErr := parseTLSConfig(ctx.Config) 91 if tlsErr != nil { 92 return nil, tlsErr 93 } 94 log, err = syslog.DialWithTLSConfig(proto, address, facility, logTag, tlsConfig) 95 } else { 96 log, err = syslog.Dial(proto, address, facility, logTag) 97 } 98 99 if err != nil { 100 return nil, err 101 } 102 103 return &syslogger{ 104 writer: log, 105 }, nil 106 } 107 108 func (s *syslogger) Log(msg *logger.Message) error { 109 if msg.Source == "stderr" { 110 return s.writer.Err(string(msg.Line)) 111 } 112 return s.writer.Info(string(msg.Line)) 113 } 114 115 func (s *syslogger) Close() error { 116 return s.writer.Close() 117 } 118 119 func (s *syslogger) Name() string { 120 return name 121 } 122 123 func parseAddress(address string) (string, string, error) { 124 if address == "" { 125 return "", "", nil 126 } 127 if !urlutil.IsTransportURL(address) { 128 return "", "", fmt.Errorf("syslog-address should be in form proto://address, got %v", address) 129 } 130 url, err := url.Parse(address) 131 if err != nil { 132 return "", "", err 133 } 134 135 // unix socket validation 136 if url.Scheme == "unix" { 137 if _, err := os.Stat(url.Path); err != nil { 138 return "", "", err 139 } 140 return url.Scheme, url.Path, nil 141 } 142 143 // here we process tcp|udp 144 host := url.Host 145 if _, _, err := net.SplitHostPort(host); err != nil { 146 if !strings.Contains(err.Error(), "missing port in address") { 147 return "", "", err 148 } 149 host = host + ":514" 150 } 151 152 return url.Scheme, host, nil 153 } 154 155 // ValidateLogOpt looks for syslog specific log options 156 // syslog-address, syslog-facility, & syslog-tag. 157 func ValidateLogOpt(cfg map[string]string) error { 158 for key := range cfg { 159 switch key { 160 case "syslog-address": 161 case "syslog-facility": 162 case "syslog-tag": 163 case "syslog-tls-ca-cert": 164 case "syslog-tls-cert": 165 case "syslog-tls-key": 166 case "syslog-tls-skip-verify": 167 case "tag": 168 default: 169 return fmt.Errorf("unknown log opt '%s' for syslog log driver", key) 170 } 171 } 172 if _, _, err := parseAddress(cfg["syslog-address"]); err != nil { 173 return err 174 } 175 if _, err := parseFacility(cfg["syslog-facility"]); err != nil { 176 return err 177 } 178 return nil 179 } 180 181 func parseFacility(facility string) (syslog.Priority, error) { 182 if facility == "" { 183 return syslog.LOG_DAEMON, nil 184 } 185 186 if syslogFacility, valid := facilities[facility]; valid { 187 return syslogFacility, nil 188 } 189 190 fInt, err := strconv.Atoi(facility) 191 if err == nil && 0 <= fInt && fInt <= 23 { 192 return syslog.Priority(fInt << 3), nil 193 } 194 195 return syslog.Priority(0), errors.New("invalid syslog facility") 196 } 197 198 func parseTLSConfig(cfg map[string]string) (*tls.Config, error) { 199 _, skipVerify := cfg["syslog-tls-skip-verify"] 200 201 opts := tlsconfig.Options{ 202 CAFile: cfg["syslog-tls-ca-cert"], 203 CertFile: cfg["syslog-tls-cert"], 204 KeyFile: cfg["syslog-tls-key"], 205 InsecureSkipVerify: skipVerify, 206 } 207 208 return tlsconfig.Client(opts) 209 }