syslog.go 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258
  1. // +build linux
  2. // Package syslog provides the logdriver for forwarding server logs to syslog endpoints.
  3. package syslog
  4. import (
  5. "crypto/tls"
  6. "errors"
  7. "fmt"
  8. "net"
  9. "net/url"
  10. "os"
  11. "strconv"
  12. "strings"
  13. "time"
  14. syslog "github.com/RackSec/srslog"
  15. "github.com/Sirupsen/logrus"
  16. "github.com/docker/docker/daemon/logger"
  17. "github.com/docker/docker/daemon/logger/loggerutils"
  18. "github.com/docker/docker/pkg/urlutil"
  19. "github.com/docker/go-connections/tlsconfig"
  20. )
  21. const (
  22. name = "syslog"
  23. secureProto = "tcp+tls"
  24. )
  25. var facilities = map[string]syslog.Priority{
  26. "kern": syslog.LOG_KERN,
  27. "user": syslog.LOG_USER,
  28. "mail": syslog.LOG_MAIL,
  29. "daemon": syslog.LOG_DAEMON,
  30. "auth": syslog.LOG_AUTH,
  31. "syslog": syslog.LOG_SYSLOG,
  32. "lpr": syslog.LOG_LPR,
  33. "news": syslog.LOG_NEWS,
  34. "uucp": syslog.LOG_UUCP,
  35. "cron": syslog.LOG_CRON,
  36. "authpriv": syslog.LOG_AUTHPRIV,
  37. "ftp": syslog.LOG_FTP,
  38. "local0": syslog.LOG_LOCAL0,
  39. "local1": syslog.LOG_LOCAL1,
  40. "local2": syslog.LOG_LOCAL2,
  41. "local3": syslog.LOG_LOCAL3,
  42. "local4": syslog.LOG_LOCAL4,
  43. "local5": syslog.LOG_LOCAL5,
  44. "local6": syslog.LOG_LOCAL6,
  45. "local7": syslog.LOG_LOCAL7,
  46. }
  47. type syslogger struct {
  48. writer *syslog.Writer
  49. }
  50. func init() {
  51. if err := logger.RegisterLogDriver(name, New); err != nil {
  52. logrus.Fatal(err)
  53. }
  54. if err := logger.RegisterLogOptValidator(name, ValidateLogOpt); err != nil {
  55. logrus.Fatal(err)
  56. }
  57. }
  58. // rsyslog uses appname part of syslog message to fill in an %syslogtag% template
  59. // attribute in rsyslog.conf. In order to be backward compatible to rfc3164
  60. // tag will be also used as an appname
  61. func rfc5424formatterWithAppNameAsTag(p syslog.Priority, hostname, tag, content string) string {
  62. timestamp := time.Now().Format(time.RFC3339)
  63. pid := os.Getpid()
  64. msg := fmt.Sprintf("<%d>%d %s %s %s %d %s %s",
  65. p, 1, timestamp, hostname, tag, pid, tag, content)
  66. return msg
  67. }
  68. // The timestamp field in rfc5424 is derived from rfc3339. Whereas rfc3339 makes allowances
  69. // for multiple syntaxes, there are further restrictions in rfc5424, i.e., the maximium
  70. // resolution is limited to "TIME-SECFRAC" which is 6 (microsecond resolution)
  71. func rfc5424microformatterWithAppNameAsTag(p syslog.Priority, hostname, tag, content string) string {
  72. timestamp := time.Now().Format("2006-01-02T15:04:05.999999Z07:00")
  73. pid := os.Getpid()
  74. msg := fmt.Sprintf("<%d>%d %s %s %s %d %s %s",
  75. p, 1, timestamp, hostname, tag, pid, tag, content)
  76. return msg
  77. }
  78. // New creates a syslog logger using the configuration passed in on
  79. // the context. Supported context configuration variables are
  80. // syslog-address, syslog-facility, syslog-format.
  81. func New(ctx logger.Context) (logger.Logger, error) {
  82. tag, err := loggerutils.ParseLogTag(ctx, loggerutils.DefaultTemplate)
  83. if err != nil {
  84. return nil, err
  85. }
  86. proto, address, err := parseAddress(ctx.Config["syslog-address"])
  87. if err != nil {
  88. return nil, err
  89. }
  90. facility, err := parseFacility(ctx.Config["syslog-facility"])
  91. if err != nil {
  92. return nil, err
  93. }
  94. syslogFormatter, syslogFramer, err := parseLogFormat(ctx.Config["syslog-format"])
  95. if err != nil {
  96. return nil, err
  97. }
  98. var log *syslog.Writer
  99. if proto == secureProto {
  100. tlsConfig, tlsErr := parseTLSConfig(ctx.Config)
  101. if tlsErr != nil {
  102. return nil, tlsErr
  103. }
  104. log, err = syslog.DialWithTLSConfig(proto, address, facility, tag, tlsConfig)
  105. } else {
  106. log, err = syslog.Dial(proto, address, facility, tag)
  107. }
  108. if err != nil {
  109. return nil, err
  110. }
  111. log.SetFormatter(syslogFormatter)
  112. log.SetFramer(syslogFramer)
  113. return &syslogger{
  114. writer: log,
  115. }, nil
  116. }
  117. func (s *syslogger) Log(msg *logger.Message) error {
  118. if msg.Source == "stderr" {
  119. return s.writer.Err(string(msg.Line))
  120. }
  121. return s.writer.Info(string(msg.Line))
  122. }
  123. func (s *syslogger) Close() error {
  124. return s.writer.Close()
  125. }
  126. func (s *syslogger) Name() string {
  127. return name
  128. }
  129. func parseAddress(address string) (string, string, error) {
  130. if address == "" {
  131. return "", "", nil
  132. }
  133. if !urlutil.IsTransportURL(address) {
  134. return "", "", fmt.Errorf("syslog-address should be in form proto://address, got %v", address)
  135. }
  136. url, err := url.Parse(address)
  137. if err != nil {
  138. return "", "", err
  139. }
  140. // unix and unixgram socket validation
  141. if url.Scheme == "unix" || url.Scheme == "unixgram" {
  142. if _, err := os.Stat(url.Path); err != nil {
  143. return "", "", err
  144. }
  145. return url.Scheme, url.Path, nil
  146. }
  147. // here we process tcp|udp
  148. host := url.Host
  149. if _, _, err := net.SplitHostPort(host); err != nil {
  150. if !strings.Contains(err.Error(), "missing port in address") {
  151. return "", "", err
  152. }
  153. host = host + ":514"
  154. }
  155. return url.Scheme, host, nil
  156. }
  157. // ValidateLogOpt looks for syslog specific log options
  158. // syslog-address, syslog-facility.
  159. func ValidateLogOpt(cfg map[string]string) error {
  160. for key := range cfg {
  161. switch key {
  162. case "env":
  163. case "labels":
  164. case "syslog-address":
  165. case "syslog-facility":
  166. case "syslog-tls-ca-cert":
  167. case "syslog-tls-cert":
  168. case "syslog-tls-key":
  169. case "syslog-tls-skip-verify":
  170. case "tag":
  171. case "syslog-format":
  172. default:
  173. return fmt.Errorf("unknown log opt '%s' for syslog log driver", key)
  174. }
  175. }
  176. if _, _, err := parseAddress(cfg["syslog-address"]); err != nil {
  177. return err
  178. }
  179. if _, err := parseFacility(cfg["syslog-facility"]); err != nil {
  180. return err
  181. }
  182. if _, _, err := parseLogFormat(cfg["syslog-format"]); err != nil {
  183. return err
  184. }
  185. return nil
  186. }
  187. func parseFacility(facility string) (syslog.Priority, error) {
  188. if facility == "" {
  189. return syslog.LOG_DAEMON, nil
  190. }
  191. if syslogFacility, valid := facilities[facility]; valid {
  192. return syslogFacility, nil
  193. }
  194. fInt, err := strconv.Atoi(facility)
  195. if err == nil && 0 <= fInt && fInt <= 23 {
  196. return syslog.Priority(fInt << 3), nil
  197. }
  198. return syslog.Priority(0), errors.New("invalid syslog facility")
  199. }
  200. func parseTLSConfig(cfg map[string]string) (*tls.Config, error) {
  201. _, skipVerify := cfg["syslog-tls-skip-verify"]
  202. opts := tlsconfig.Options{
  203. CAFile: cfg["syslog-tls-ca-cert"],
  204. CertFile: cfg["syslog-tls-cert"],
  205. KeyFile: cfg["syslog-tls-key"],
  206. InsecureSkipVerify: skipVerify,
  207. }
  208. return tlsconfig.Client(opts)
  209. }
  210. func parseLogFormat(logFormat string) (syslog.Formatter, syslog.Framer, error) {
  211. switch logFormat {
  212. case "":
  213. return syslog.UnixFormatter, syslog.DefaultFramer, nil
  214. case "rfc3164":
  215. return syslog.RFC3164Formatter, syslog.DefaultFramer, nil
  216. case "rfc5424":
  217. return rfc5424formatterWithAppNameAsTag, syslog.RFC5425MessageLengthFramer, nil
  218. case "rfc5424micro":
  219. return rfc5424microformatterWithAppNameAsTag, syslog.RFC5425MessageLengthFramer, nil
  220. default:
  221. return nil, nil, errors.New("Invalid syslog format")
  222. }
  223. }