Преглед изворни кода

Merge pull request #18998 from calavera/syslog_tls

Allow syslog over TCP+TLS.
Sebastiaan van Stijn пре 9 година
родитељ
комит
656979670c

+ 38 - 8
daemon/logger/syslog/syslog.go

@@ -4,9 +4,9 @@
 package syslog
 package syslog
 
 
 import (
 import (
+	"crypto/tls"
 	"errors"
 	"errors"
 	"fmt"
 	"fmt"
-	"log/syslog"
 	"net"
 	"net"
 	"net/url"
 	"net/url"
 	"os"
 	"os"
@@ -14,13 +14,19 @@ import (
 	"strconv"
 	"strconv"
 	"strings"
 	"strings"
 
 
+	syslog "github.com/RackSec/srslog"
+
 	"github.com/Sirupsen/logrus"
 	"github.com/Sirupsen/logrus"
 	"github.com/docker/docker/daemon/logger"
 	"github.com/docker/docker/daemon/logger"
 	"github.com/docker/docker/daemon/logger/loggerutils"
 	"github.com/docker/docker/daemon/logger/loggerutils"
 	"github.com/docker/docker/pkg/urlutil"
 	"github.com/docker/docker/pkg/urlutil"
+	"github.com/docker/go-connections/tlsconfig"
 )
 )
 
 
-const name = "syslog"
+const (
+	name        = "syslog"
+	secureProto = "tcp+tls"
+)
 
 
 var facilities = map[string]syslog.Priority{
 var facilities = map[string]syslog.Priority{
 	"kern":     syslog.LOG_KERN,
 	"kern":     syslog.LOG_KERN,
@@ -77,12 +83,19 @@ func New(ctx logger.Context) (logger.Logger, error) {
 		return nil, err
 		return nil, err
 	}
 	}
 
 
-	log, err := syslog.Dial(
-		proto,
-		address,
-		facility,
-		path.Base(os.Args[0])+"/"+tag,
-	)
+	logTag := path.Base(os.Args[0]) + "/" + tag
+
+	var log *syslog.Writer
+	if proto == secureProto {
+		tlsConfig, tlsErr := parseTLSConfig(ctx.Config)
+		if tlsErr != nil {
+			return nil, tlsErr
+		}
+		log, err = syslog.DialWithTLSConfig(proto, address, facility, logTag, tlsConfig)
+	} else {
+		log, err = syslog.Dial(proto, address, facility, logTag)
+	}
+
 	if err != nil {
 	if err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
@@ -147,6 +160,10 @@ func ValidateLogOpt(cfg map[string]string) error {
 		case "syslog-address":
 		case "syslog-address":
 		case "syslog-facility":
 		case "syslog-facility":
 		case "syslog-tag":
 		case "syslog-tag":
+		case "syslog-tls-ca-cert":
+		case "syslog-tls-cert":
+		case "syslog-tls-key":
+		case "syslog-tls-skip-verify":
 		case "tag":
 		case "tag":
 		default:
 		default:
 			return fmt.Errorf("unknown log opt '%s' for syslog log driver", key)
 			return fmt.Errorf("unknown log opt '%s' for syslog log driver", key)
@@ -177,3 +194,16 @@ func parseFacility(facility string) (syslog.Priority, error) {
 
 
 	return syslog.Priority(0), errors.New("invalid syslog facility")
 	return syslog.Priority(0), errors.New("invalid syslog facility")
 }
 }
+
+func parseTLSConfig(cfg map[string]string) (*tls.Config, error) {
+	_, skipVerify := cfg["syslog-tls-skip-verify"]
+
+	opts := tlsconfig.Options{
+		CAFile:             cfg["syslog-tls-ca-cert"],
+		CertFile:           cfg["syslog-tls-cert"],
+		KeyFile:            cfg["syslog-tls-key"],
+		InsecureSkipVerify: skipVerify,
+	}
+
+	return tlsconfig.Client(opts)
+}

+ 18 - 1
docs/reference/logging/overview.md

@@ -69,9 +69,13 @@ If `max-size` and `max-file` are set, `docker logs` only returns the log lines f
 
 
 The following logging options are supported for the `syslog` logging driver:
 The following logging options are supported for the `syslog` logging driver:
 
 
-    --log-opt syslog-address=[tcp|udp]://host:port
+    --log-opt syslog-address=[tcp|udp|tcp+tls]://host:port
     --log-opt syslog-address=unix://path
     --log-opt syslog-address=unix://path
     --log-opt syslog-facility=daemon
     --log-opt syslog-facility=daemon
+    --log-opt syslog-tls-ca-cert=/etc/ca-certificates/custom/ca.pem
+    --log-opt syslog-tls-cert=/etc/ca-certificates/custom/cert.pem
+    --log-opt syslog-tls-key=/etc/ca-certificates/custom/key.pem
+    --log-opt syslog-tls-skip-verify=true
     --log-opt tag="mailer"
     --log-opt tag="mailer"
 
 
 `syslog-address` specifies the remote syslog server address where the driver connects to.
 `syslog-address` specifies the remote syslog server address where the driver connects to.
@@ -107,6 +111,19 @@ the following named facilities:
 * `local6`
 * `local6`
 * `local7`
 * `local7`
 
 
+`syslog-tls-ca-cert` specifies the absolute path to the trust certificates
+signed by the CA. This option is ignored if the address protocol is not `tcp+tls`.
+
+`syslog-tls-cert` specifies the absolute path to the TLS certificate file.
+This option is ignored if the address protocol is not `tcp+tls`.
+
+`syslog-tls-key` specifies the absolute path to the TLS key file.
+This option is ignored if the address protocol is not `tcp+tls`.
+
+`syslog-tls-skip-verify` configures the TLS verification.
+This verification is enabled by default, but it can be overriden by setting
+this option to `true`. This option is ignored if the address protocol is not `tcp+tls`.
+
 By default, Docker uses the first 12 characters of the container ID to tag log messages.
 By default, Docker uses the first 12 characters of the container ID to tag log messages.
 Refer to the [log tag option documentation](log_tags.md) for customizing
 Refer to the [log tag option documentation](log_tags.md) for customizing
 the log tag format.
 the log tag format.

+ 1 - 0
hack/vendor.sh

@@ -23,6 +23,7 @@ clone git golang.org/x/net 47990a1ba55743e6ef1affd3a14e5bac8553615d https://gith
 clone git github.com/docker/go-units 651fc226e7441360384da338d0fd37f2440ffbe3
 clone git github.com/docker/go-units 651fc226e7441360384da338d0fd37f2440ffbe3
 clone git github.com/docker/go-connections v0.1.2
 clone git github.com/docker/go-connections v0.1.2
 clone git github.com/docker/engine-api v0.1.3
 clone git github.com/docker/engine-api v0.1.3
+clone git github.com/RackSec/srslog 6eb773f331e46fbba8eecb8e794e635e75fc04de
 
 
 #get libnetwork packages
 #get libnetwork packages
 clone git github.com/docker/libnetwork c8ec4bd24e1e76feb4f79e3924c68cd2ce89938a
 clone git github.com/docker/libnetwork c8ec4bd24e1e76feb4f79e3924c68cd2ce89938a

+ 1 - 0
vendor/src/github.com/RackSec/srslog/.gitignore

@@ -0,0 +1 @@
+.cover

+ 13 - 0
vendor/src/github.com/RackSec/srslog/.travis.yml

@@ -0,0 +1,13 @@
+sudo: required
+dist: trusty
+group: edge
+language: go
+go:
+- 1.5
+script:
+- |
+  go get ./...
+  go test -v ./...
+notifications:
+  slack:
+    secure: dtDue9gP6CRR1jYjEf6raXXFak3QKGcCFvCf5mfvv5XScdpmc3udwgqc5TdyjC0goaC9OK/4jTcCD30dYZm/u6ux3E9mo3xwMl2xRLHx76p5r9rSQtloH19BDwA2+A+bpDfFQVz05k2YXuTiGSvNMMdwzx+Dr294Sl/z43RFB4+b9/R/6LlFpRW89IwftvpLAFnBy4K/ZcspQzKM+rQfQTL5Kk+iZ/KBsuR/VziDq6MoJ8t43i4ee8vwS06vFBKDbUiZ4FIZpLgc2RAL5qso5aWRKYXL6waXfoKHZWKPe0w4+9IY1rDJxG1jEb7YGgcbLaF9xzPRRs2b2yO/c87FKpkh6PDgYHfLjpgXotCoojZrL4p1x6MI1ldJr3NhARGPxS9r4liB9n6Y5nD+ErXi1IMf55fuUHcPY27Jc0ySeLFeM6cIWJ8OhFejCgGw6a5DnnmJo0PqopsaBDHhadpLejT1+K6bL2iGkT4SLcVNuRGLs+VyuNf1+5XpkWZvy32vquO7SZOngLLBv+GIem+t3fWm0Z9s/0i1uRCQei1iUutlYjoV/LBd35H2rhob4B5phIuJin9kb0zbHf6HnaoN0CtN8r0d8G5CZiInVlG5Xcid5Byb4dddf5U2EJTDuCMVyyiM7tcnfjqw9UbVYNxtYM9SzcqIq+uVqM8pYL9xSec=

+ 50 - 0
vendor/src/github.com/RackSec/srslog/CODE_OF_CONDUCT.md

@@ -0,0 +1,50 @@
+# Contributor Code of Conduct
+
+As contributors and maintainers of this project, and in the interest of
+fostering an open and welcoming community, we pledge to respect all people who
+contribute through reporting issues, posting feature requests, updating
+documentation, submitting pull requests or patches, and other activities.
+
+We are committed to making participation in this project a harassment-free
+experience for everyone, regardless of level of experience, gender, gender
+identity and expression, sexual orientation, disability, personal appearance,
+body size, race, ethnicity, age, religion, or nationality.
+
+Examples of unacceptable behavior by participants include:
+
+* The use of sexualized language or imagery
+* Personal attacks
+* Trolling or insulting/derogatory comments
+* Public or private harassment
+* Publishing other's private information, such as physical or electronic
+  addresses, without explicit permission
+* Other unethical or unprofessional conduct
+
+Project maintainers have the right and responsibility to remove, edit, or
+reject comments, commits, code, wiki edits, issues, and other contributions
+that are not aligned to this Code of Conduct, or to ban temporarily or
+permanently any contributor for other behaviors that they deem inappropriate,
+threatening, offensive, or harmful.
+
+By adopting this Code of Conduct, project maintainers commit themselves to
+fairly and consistently applying these principles to every aspect of managing
+this project. Project maintainers who do not follow or enforce the Code of
+Conduct may be permanently removed from the project team.
+
+This Code of Conduct applies both within project spaces and in public spaces
+when an individual is representing the project or its community.
+
+Instances of abusive, harassing, or otherwise unacceptable behavior may be
+reported by contacting a project maintainer at [sirsean@gmail.com]. All
+complaints will be reviewed and investigated and will result in a response that
+is deemed necessary and appropriate to the circumstances. Maintainers are
+obligated to maintain confidentiality with regard to the reporter of an
+incident.
+
+
+This Code of Conduct is adapted from the [Contributor Covenant][homepage],
+version 1.3.0, available at
+[http://contributor-covenant.org/version/1/3/0/][version]
+
+[homepage]: http://contributor-covenant.org
+[version]: http://contributor-covenant.org/version/1/3/0/

+ 27 - 0
vendor/src/github.com/RackSec/srslog/LICENSE

@@ -0,0 +1,27 @@
+Copyright (c) 2015 Rackspace. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+   * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+   * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+   * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

+ 131 - 0
vendor/src/github.com/RackSec/srslog/README.md

@@ -0,0 +1,131 @@
+[![Build Status](https://travis-ci.org/RackSec/srslog.svg?branch=master)](https://travis-ci.org/RackSec/srslog)
+
+# srslog
+
+Go has a `syslog` package in the standard library, but it has the following
+shortcomings:
+
+1. It doesn't have TLS support
+2. [According to bradfitz on the Go team, it is no longer being maintained.](https://github.com/golang/go/issues/13449#issuecomment-161204716)
+
+I agree that it doesn't need to be in the standard library. So, I've
+followed Brad's suggestion and have made a separate project to handle syslog.
+
+This code was taken directly from the Go project as a base to start from.
+
+However, this _does_ have TLS support.
+
+# Usage
+
+Basic usage retains the same interface as the original `syslog` package. We
+only added to the interface where required to support new functionality.
+
+Switch from the standard library:
+
+```
+import(
+    //"log/syslog"
+    syslog "github.com/RackSec/srslog"
+)
+```
+
+You can still use it for local syslog:
+
+```
+w, err := syslog.Dial("", "", syslog.LOG_ERR, "testtag")
+```
+
+Or to unencrypted UDP:
+
+```
+w, err := syslog.Dial("udp", "192.168.0.50:514", syslog.LOG_ERR, "testtag")
+```
+
+Or to unencrypted TCP:
+
+```
+w, err := syslog.Dial("tcp", "192.168.0.51:514", syslog.LOG_ERR, "testtag")
+```
+
+But now you can also send messages via TLS-encrypted TCP:
+
+```
+w, err := syslog.DialWithTLSCertPath("tcp+tls", "192.168.0.52:514", syslog.LOG_ERR, "testtag", "/path/to/servercert.pem")
+```
+
+And if you need more control over your TLS configuration :
+
+```
+pool := x509.NewCertPool()
+serverCert, err := ioutil.ReadFile("/path/to/servercert.pem")
+if err != nil {
+    return nil, err
+}
+pool.AppendCertsFromPEM(serverCert)
+config := tls.Config{
+    RootCAs: pool,
+}
+
+w, err := DialWithTLSConfig(network, raddr, priority, tag, &config)
+```
+
+(Note that in both TLS cases, this uses a self-signed certificate, where the
+remote syslog server has the keypair and the client has only the public key.)
+
+And then to write log messages, continue like so:
+
+```
+if err != nil {
+    log.Fatal("failed to connect to syslog:", err)
+}
+defer w.Close()
+
+w.Alert("this is an alert")
+w.Crit("this is critical")
+w.Err("this is an error")
+w.Warning("this is a warning")
+w.Notice("this is a notice")
+w.Info("this is info")
+w.Debug("this is debug")
+w.Write([]byte("these are some bytes"))
+```
+
+# Generating TLS Certificates
+
+We've provided a script that you can use to generate a self-signed keypair:
+
+```
+pip install cryptography
+python script/gen-certs.py
+```
+
+That outputs the public key and private key to standard out. Put those into
+`.pem` files. (And don't put them into any source control. The certificate in
+the `test` directory is used by the unit tests, and please do not actually use
+it anywhere else.)
+
+# Running Tests
+
+Run the tests as usual:
+
+```
+go test
+```
+
+But we've also provided a test coverage script that will show you which
+lines of code are not covered:
+
+```
+script/coverage --html
+```
+
+That will open a new browser tab showing coverage information.
+
+# License
+
+This project uses the New BSD License, the same as the Go project itself.
+
+# Code of Conduct
+
+Please note that this project is released with a Contributor Code of Conduct.
+By participating in this project you agree to abide by its terms.

+ 68 - 0
vendor/src/github.com/RackSec/srslog/constants.go

@@ -0,0 +1,68 @@
+package srslog
+
+import (
+	"errors"
+)
+
+// Priority is a combination of the syslog facility and
+// severity. For example, LOG_ALERT | LOG_FTP sends an alert severity
+// message from the FTP facility. The default severity is LOG_EMERG;
+// the default facility is LOG_KERN.
+type Priority int
+
+const severityMask = 0x07
+const facilityMask = 0xf8
+
+const (
+	// Severity.
+
+	// From /usr/include/sys/syslog.h.
+	// These are the same on Linux, BSD, and OS X.
+	LOG_EMERG Priority = iota
+	LOG_ALERT
+	LOG_CRIT
+	LOG_ERR
+	LOG_WARNING
+	LOG_NOTICE
+	LOG_INFO
+	LOG_DEBUG
+)
+
+const (
+	// Facility.
+
+	// From /usr/include/sys/syslog.h.
+	// These are the same up to LOG_FTP on Linux, BSD, and OS X.
+	LOG_KERN Priority = iota << 3
+	LOG_USER
+	LOG_MAIL
+	LOG_DAEMON
+	LOG_AUTH
+	LOG_SYSLOG
+	LOG_LPR
+	LOG_NEWS
+	LOG_UUCP
+	LOG_CRON
+	LOG_AUTHPRIV
+	LOG_FTP
+	_ // unused
+	_ // unused
+	_ // unused
+	_ // unused
+	LOG_LOCAL0
+	LOG_LOCAL1
+	LOG_LOCAL2
+	LOG_LOCAL3
+	LOG_LOCAL4
+	LOG_LOCAL5
+	LOG_LOCAL6
+	LOG_LOCAL7
+)
+
+func validatePriority(p Priority) error {
+	if p < 0 || p > LOG_LOCAL7|LOG_DEBUG {
+		return errors.New("log/syslog: invalid priority")
+	} else {
+		return nil
+	}
+}

+ 53 - 0
vendor/src/github.com/RackSec/srslog/dialer.go

@@ -0,0 +1,53 @@
+package srslog
+
+import (
+	"crypto/tls"
+	"net"
+)
+
+func (w Writer) getDialer() func() (serverConn, string, error) {
+	dialers := map[string]func() (serverConn, string, error){
+		"":        w.unixDialer,
+		"tcp+tls": w.tlsDialer,
+	}
+	dialer, ok := dialers[w.network]
+	if !ok {
+		dialer = w.basicDialer
+	}
+	return dialer
+}
+
+func (w Writer) unixDialer() (serverConn, string, error) {
+	sc, err := unixSyslog()
+	hostname := w.hostname
+	if hostname == "" {
+		hostname = "localhost"
+	}
+	return sc, hostname, err
+}
+
+func (w Writer) tlsDialer() (serverConn, string, error) {
+	c, err := tls.Dial("tcp", w.raddr, w.tlsConfig)
+	var sc serverConn
+	hostname := w.hostname
+	if err == nil {
+		sc = &netConn{conn: c}
+		if hostname == "" {
+			hostname = c.LocalAddr().String()
+		}
+	}
+	return sc, hostname, err
+}
+
+func (w Writer) basicDialer() (serverConn, string, error) {
+	c, err := net.Dial(w.network, w.raddr)
+	var sc serverConn
+	hostname := w.hostname
+	if err == nil {
+		sc = &netConn{conn: c}
+		if hostname == "" {
+			hostname = c.LocalAddr().String()
+		}
+	}
+	return sc, hostname, err
+}

+ 24 - 0
vendor/src/github.com/RackSec/srslog/net_conn.go

@@ -0,0 +1,24 @@
+package srslog
+
+import (
+	"fmt"
+	"net"
+	"os"
+	"time"
+)
+
+type netConn struct {
+	conn net.Conn
+}
+
+func (n *netConn) writeString(p Priority, hostname, tag, msg string) error {
+	timestamp := time.Now().Format(time.RFC3339)
+	_, err := fmt.Fprintf(n.conn, "<%d>%s %s %s[%d]: %s",
+		p, timestamp, hostname,
+		tag, os.Getpid(), msg)
+	return err
+}
+
+func (n *netConn) close() error {
+	return n.conn.Close()
+}

+ 96 - 0
vendor/src/github.com/RackSec/srslog/srslog.go

@@ -0,0 +1,96 @@
+package srslog
+
+import (
+	"crypto/tls"
+	"crypto/x509"
+	"io/ioutil"
+	"log"
+	"os"
+)
+
+// This interface and the separate syslog_unix.go file exist for
+// Solaris support as implemented by gccgo.  On Solaris you can not
+// simply open a TCP connection to the syslog daemon.  The gccgo
+// sources have a syslog_solaris.go file that implements unixSyslog to
+// return a type that satisfies this interface and simply calls the C
+// library syslog function.
+type serverConn interface {
+	writeString(p Priority, hostname, tag, s string) error
+	close() error
+}
+
+// New establishes a new connection to the system log daemon.  Each
+// write to the returned Writer sends a log message with the given
+// priority and prefix.
+func New(priority Priority, tag string) (w *Writer, err error) {
+	return Dial("", "", priority, tag)
+}
+
+// Dial establishes a connection to a log daemon by connecting to
+// address raddr on the specified network.  Each write to the returned
+// Writer sends a log message with the given facility, severity and
+// tag.
+// If network is empty, Dial will connect to the local syslog server.
+func Dial(network, raddr string, priority Priority, tag string) (*Writer, error) {
+	return DialWithTLSConfig(network, raddr, priority, tag, nil)
+}
+
+// DialWithTLSCertPath establishes a secure connection to a log daemon by connecting to
+// address raddr on the specified network. It uses certPath to load TLS certificates and configure
+// the secure connection.
+func DialWithTLSCertPath(network, raddr string, priority Priority, tag, certPath string) (*Writer, error) {
+	pool := x509.NewCertPool()
+	serverCert, err := ioutil.ReadFile(certPath)
+	if err != nil {
+		return nil, err
+	}
+	pool.AppendCertsFromPEM(serverCert)
+	config := tls.Config{
+		RootCAs: pool,
+	}
+
+	return DialWithTLSConfig(network, raddr, priority, tag, &config)
+}
+
+// DialWithTLSConfig establishes a secure connection to a log daemon by connecting to
+// address raddr on the specified network. It uses tlsConfig to configure the secure connection.
+func DialWithTLSConfig(network, raddr string, priority Priority, tag string, tlsConfig *tls.Config) (*Writer, error) {
+	if err := validatePriority(priority); err != nil {
+		return nil, err
+	}
+
+	if tag == "" {
+		tag = os.Args[0]
+	}
+	hostname, _ := os.Hostname()
+
+	w := &Writer{
+		priority:  priority,
+		tag:       tag,
+		hostname:  hostname,
+		network:   network,
+		raddr:     raddr,
+		tlsConfig: tlsConfig,
+	}
+
+	w.Lock()
+	defer w.Unlock()
+
+	err := w.connect()
+	if err != nil {
+		return nil, err
+	}
+	return w, err
+}
+
+// NewLogger creates a log.Logger whose output is written to
+// the system log service with the specified priority. The logFlag
+// argument is the flag set passed through to log.New to create
+// the Logger.
+func NewLogger(p Priority, logFlag int) (*log.Logger, error) {
+	s, err := New(p, "")
+	if err != nil {
+		return nil, err
+	}
+	return log.New(s, "", logFlag), nil
+}

+ 47 - 0
vendor/src/github.com/RackSec/srslog/srslog_unix.go

@@ -0,0 +1,47 @@
+package srslog
+
+import (
+	"errors"
+	"fmt"
+	"net"
+	"os"
+	"time"
+)
+
+// unixSyslog opens a connection to the syslog daemon running on the
+// local machine using a Unix domain socket.
+
+func unixSyslog() (conn serverConn, err error) {
+	logTypes := []string{"unixgram", "unix"}
+	logPaths := []string{"/dev/log", "/var/run/syslog", "/var/run/log"}
+	for _, network := range logTypes {
+		for _, path := range logPaths {
+			conn, err := net.Dial(network, path)
+			if err != nil {
+				continue
+			} else {
+				return &localConn{conn: conn}, nil
+			}
+		}
+	}
+	return nil, errors.New("Unix syslog delivery error")
+}
+
+type localConn struct {
+	conn net.Conn
+}
+
+func (n *localConn) writeString(p Priority, hostname, tag, msg string) error {
+	// Compared to the network form at srslog.netConn, the changes are:
+	//	1. Use time.Stamp instead of time.RFC3339.
+	//	2. Drop the hostname field from the Fprintf.
+	timestamp := time.Now().Format(time.Stamp)
+	_, err := fmt.Fprintf(n.conn, "<%d>%s %s[%d]: %s",
+		p, timestamp,
+		tag, os.Getpid(), msg)
+	return err
+}
+
+func (n *localConn) close() error {
+	return n.conn.Close()
+}

+ 152 - 0
vendor/src/github.com/RackSec/srslog/writer.go

@@ -0,0 +1,152 @@
+package srslog
+
+import (
+	"crypto/tls"
+	"strings"
+	"sync"
+)
+
+// A Writer is a connection to a syslog server.
+type Writer struct {
+	sync.Mutex // guards conn
+
+	priority  Priority
+	tag       string
+	hostname  string
+	network   string
+	raddr     string
+	tlsConfig *tls.Config
+
+	conn serverConn
+}
+
+// connect makes a connection to the syslog server.
+// It must be called with w.mu held.
+func (w *Writer) connect() (err error) {
+	if w.conn != nil {
+		// ignore err from close, it makes sense to continue anyway
+		w.conn.close()
+		w.conn = nil
+	}
+
+	var conn serverConn
+	var hostname string
+	dialer := w.getDialer()
+	conn, hostname, err = dialer()
+	if err == nil {
+		w.conn = conn
+		w.hostname = hostname
+	}
+
+	return
+}
+
+// Write sends a log message to the syslog daemon using the default priority
+// passed into `srslog.New` or the `srslog.Dial*` functions.
+func (w *Writer) Write(b []byte) (int, error) {
+	return w.writeAndRetry(w.priority, string(b))
+}
+
+// Close closes a connection to the syslog daemon.
+func (w *Writer) Close() error {
+	w.Lock()
+	defer w.Unlock()
+
+	if w.conn != nil {
+		err := w.conn.close()
+		w.conn = nil
+		return err
+	}
+	return nil
+}
+
+// Emerg logs a message with severity LOG_EMERG; this overrides the default
+// priority passed to `srslog.New` and the `srslog.Dial*` functions.
+func (w *Writer) Emerg(m string) (err error) {
+	_, err = w.writeAndRetry(LOG_EMERG, m)
+	return err
+}
+
+// Alert logs a message with severity LOG_ALERT; this overrides the default
+// priority passed to `srslog.New` and the `srslog.Dial*` functions.
+func (w *Writer) Alert(m string) (err error) {
+	_, err = w.writeAndRetry(LOG_ALERT, m)
+	return err
+}
+
+// Crit logs a message with severity LOG_CRIT; this overrides the default
+// priority passed to `srslog.New` and the `srslog.Dial*` functions.
+func (w *Writer) Crit(m string) (err error) {
+	_, err = w.writeAndRetry(LOG_CRIT, m)
+	return err
+}
+
+// Err logs a message with severity LOG_ERR; this overrides the default
+// priority passed to `srslog.New` and the `srslog.Dial*` functions.
+func (w *Writer) Err(m string) (err error) {
+	_, err = w.writeAndRetry(LOG_ERR, m)
+	return err
+}
+
+// Warning logs a message with severity LOG_WARNING; this overrides the default
+// priority passed to `srslog.New` and the `srslog.Dial*` functions.
+func (w *Writer) Warning(m string) (err error) {
+	_, err = w.writeAndRetry(LOG_WARNING, m)
+	return err
+}
+
+// Notice logs a message with severity LOG_NOTICE; this overrides the default
+// priority passed to `srslog.New` and the `srslog.Dial*` functions.
+func (w *Writer) Notice(m string) (err error) {
+	_, err = w.writeAndRetry(LOG_NOTICE, m)
+	return err
+}
+
+// Info logs a message with severity LOG_INFO; this overrides the default
+// priority passed to `srslog.New` and the `srslog.Dial*` functions.
+func (w *Writer) Info(m string) (err error) {
+	_, err = w.writeAndRetry(LOG_INFO, m)
+	return err
+}
+
+// Debug logs a message with severity LOG_DEBUG; this overrides the default
+// priority passed to `srslog.New` and the `srslog.Dial*` functions.
+func (w *Writer) Debug(m string) (err error) {
+	_, err = w.writeAndRetry(LOG_DEBUG, m)
+	return err
+}
+
+func (w *Writer) writeAndRetry(p Priority, s string) (int, error) {
+	pr := (w.priority & facilityMask) | (p & severityMask)
+
+	w.Lock()
+	defer w.Unlock()
+
+	if w.conn != nil {
+		if n, err := w.write(pr, s); err == nil {
+			return n, err
+		}
+	}
+	if err := w.connect(); err != nil {
+		return 0, err
+	}
+	return w.write(pr, s)
+}
+
+// write generates and writes a syslog formatted string. The
+// format is as follows: <PRI>TIMESTAMP HOSTNAME TAG[PID]: MSG
+func (w *Writer) write(p Priority, msg string) (int, error) {
+	// ensure it ends in a \n
+	if !strings.HasSuffix(msg, "\n") {
+		msg += "\n"
+	}
+
+	err := w.conn.writeString(p, w.hostname, w.tag, msg)
+	if err != nil {
+		return 0, err
+	}
+	// Note: return the length of the input, not the number of
+	// bytes printed by Fprintf, because this must behave like
+	// an io.Writer.
+	return len(msg), nil
+}