-
Notifications
You must be signed in to change notification settings - Fork 14
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
new syslog server with tcp and tls support, added tests
- Loading branch information
Marcel Gebhardt
committed
Apr 20, 2018
1 parent
f5b5ead
commit d161969
Showing
3 changed files
with
289 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
language: go | ||
go_import_path: github.com/Neo23x0/simplesyslog | ||
notifications: | ||
email: false | ||
slack: nextron-systems:Q1f2uRSIFH0Sf5IzT884z4YR |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,111 @@ | ||
// Simple Syslog Server that | ||
// supports UDP, TCP and TLS. | ||
// | ||
// Marcel Gebhardt | ||
// April 2018 | ||
|
||
package simplesyslog | ||
|
||
import ( | ||
"crypto/tls" | ||
"fmt" | ||
"log/syslog" | ||
"net" | ||
"os" | ||
"time" | ||
) | ||
|
||
// ConnectionType defines wheather to connect via UDP or TCP (or TLS) | ||
type ConnectionType string | ||
|
||
const ( | ||
// ConnectionUDP connects via UDP | ||
ConnectionUDP ConnectionType = "udp" | ||
// ConnectionTCP connects via TCP | ||
ConnectionTCP ConnectionType = "tcp" | ||
// ConnectionTLS connects via TLS | ||
ConnectionTLS ConnectionType = "tls" | ||
) | ||
|
||
const ( | ||
// DefaultHostname will be used if hostname could not be determined | ||
DefaultHostname string = "unknown" | ||
// DefaultIP will be used if ip could not be determined | ||
DefaultIP string = "" | ||
) | ||
|
||
// Server holds a connection to a specified address | ||
type Server struct { | ||
Hostname string // Hostname of the system | ||
IP string // IP of the system | ||
Rfc3164 bool // rfc standard for length reduction | ||
Rfc5424 bool // rfc standard for length reduction | ||
conn net.Conn // connection to the syslog server | ||
} | ||
|
||
// NewServer initializes a new server connection. | ||
// Examples: | ||
// - NewServer(ConnectionUDP, "172.0.0.1:514") | ||
// - NewServer(ConnectionTCP, ":514") | ||
// - NewServer(ConnectionTLS, "172.0.0.1:514") | ||
func NewServer(connectionType ConnectionType, address string) (*Server, error) { | ||
// Validate data | ||
if connectionType != ConnectionUDP && connectionType != ConnectionTCP && connectionType != ConnectionTLS { | ||
return nil, fmt.Errorf("unknown connection type '%s'", connectionType) | ||
} | ||
var ( | ||
conn net.Conn | ||
err error | ||
) | ||
// connect via udp / tcp / tls | ||
if connectionType == ConnectionTLS { | ||
conn, err = tls.Dial(string(ConnectionTCP), address, &tls.Config{ | ||
InsecureSkipVerify: true, | ||
}) | ||
} else { | ||
conn, err = net.Dial(string(connectionType), address) | ||
} | ||
if err != nil { | ||
return nil, err | ||
} | ||
// get hostname and ip of system | ||
hostname, err := os.Hostname() | ||
if err != nil { | ||
hostname = DefaultHostname | ||
} | ||
ip, _, err := net.SplitHostPort(conn.LocalAddr().String()) | ||
if err != nil { | ||
ip = DefaultIP | ||
} | ||
// return the server | ||
return &Server{ | ||
Hostname: hostname, | ||
IP: ip, | ||
conn: conn, | ||
}, nil | ||
} | ||
|
||
// Send sends a syslog message with a specified priority. | ||
// Examples: | ||
// - Send("foo", syslog.LOG_LOCAL0|syslog.LOG_NOTICE) | ||
// - Send("bar", syslog.LOG_DAEMON|syslog.LOG_DEBUG) | ||
func (server *Server) Send(message string, priority syslog.Priority) error { | ||
timestamp := time.Now().Format("Jan _2 15:04:05") | ||
hostnameCombi := fmt.Sprintf("%s/%s", server.Hostname, server.IP) | ||
header := fmt.Sprintf("<%d>%s %s", int(priority), timestamp, hostnameCombi) | ||
// RFC length reduction | ||
if server.Rfc3164 && len(message) > 1024 { | ||
message = fmt.Sprintf("%s...", message[:1020]) | ||
} | ||
if server.Rfc5424 && len(message) > 2048 { | ||
message = fmt.Sprintf("%s...", message[:2044]) | ||
} | ||
// Send message | ||
_, err := fmt.Fprintf(server.conn, "%s %s", header, message) | ||
return err | ||
} | ||
|
||
// Close closes the server connection gracefully. | ||
func (server *Server) Close() error { | ||
return server.conn.Close() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,173 @@ | ||
package simplesyslog | ||
|
||
import ( | ||
"crypto/tls" | ||
"crypto/x509" | ||
"encoding/pem" | ||
"io/ioutil" | ||
"log/syslog" | ||
"net" | ||
"regexp" | ||
"strconv" | ||
"testing" | ||
"time" | ||
) | ||
|
||
const host = "127.0.0.1:15140" | ||
|
||
const tlsCRT = `-----BEGIN CERTIFICATE----- | ||
MIICJzCCAZACCQCPGY+4vjNV0TANBgkqhkiG9w0BAQUFADBYMQswCQYDVQQGEwJE | ||
RTEPMA0GA1UECAwGSGVzc2VuMRIwEAYDVQQHDAlGcmFua2Z1cnQxEjAQBgNVBAoM | ||
CUNvZGVoYXJkdDEQMA4GA1UEAwwHVGVzdGluZzAeFw0xODA0MjAwNzUwMzZaFw0x | ||
OTA0MjEwNzUwMzZaMFgxCzAJBgNVBAYTAkRFMQ8wDQYDVQQIDAZIZXNzZW4xEjAQ | ||
BgNVBAcMCUZyYW5rZnVydDESMBAGA1UECgwJQ29kZWhhcmR0MRAwDgYDVQQDDAdU | ||
ZXN0aW5nMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDcHWCPUdd4VfxorTjk | ||
5g/97HiAdcVn2kbDEVv2aI9HdSbcC9DC209DoaX4/7+3cEf3TwE5RKu9acf8tDue | ||
W8tAWvKH4wW7hIHiipfhFisuQeLe5NgXGqY+bs+B5+A0C/rKTrGkHu8hpXjFPY2y | ||
rwyYMBPmIm44X53tzsNYzuQakQIDAQABMA0GCSqGSIb3DQEBBQUAA4GBAL5k/7HU | ||
g2+6QcrV2K+2616D0ssgFrKqyG1dTy9w2+jZPQWcVNTRXGDkfdK/JlXzfEQwI/bZ | ||
89GmxswWxoxoMi7ZMPAG2h66vMUwCFTjUGEihgX/qksnzglMTQHlgLGENfQfawy1 | ||
G1MpWJaW2ClQGr70dTTFeFJLSOANdAEqkTOC | ||
-----END CERTIFICATE-----` | ||
|
||
const tlsKEY = `-----BEGIN RSA PRIVATE KEY----- | ||
MIICXgIBAAKBgQDcHWCPUdd4VfxorTjk5g/97HiAdcVn2kbDEVv2aI9HdSbcC9DC | ||
209DoaX4/7+3cEf3TwE5RKu9acf8tDueW8tAWvKH4wW7hIHiipfhFisuQeLe5NgX | ||
GqY+bs+B5+A0C/rKTrGkHu8hpXjFPY2yrwyYMBPmIm44X53tzsNYzuQakQIDAQAB | ||
AoGBAI8JmCIKcRcF6YysZHh6+JFuBbCU179xHOLOeRBbSiCJhMMh+ntlwNCWTyDM | ||
MW2nTVzsvkLU2TWxdABHryZtSFpJIq69euzRtEN3uFy5qJWGvlE6Tn+ps7XIbPsX | ||
rppeclgV7a2nznrh+v1hfY/hgePyhuDsH0Hh5HB+L/gdb5DxAkEA81IPoy/UZWKg | ||
wIGeGy0mWyb4rmIxJTxOQPHd+2iJaD173eY0PskWuMLHFjyVn2gqHdy88ZDe0Xh9 | ||
35SjOw5J7wJBAOeVvye1hnvOdgOHpuurDwvoTy/A+hUhzuzPKyUhNwENHk1d5L8D | ||
w2n+onITuFhRUvJW+ZCn+8BcY2Q0FNUqY38CQQCs7WhhsQ+BkqvuxPAKHneBFtxs | ||
iyqkbQysiXkbQXtOk0viM8ZzzNSSMRPvENXBufUczhGWmUBSnRDQgsHTqd8PAkAv | ||
Wdbz75HHzrcikaH3nco9zQoj4XlAyODeWp2fweLVPDFt8DzNMZ/LFF1ypcWTiU1E | ||
b7Qnd7Fp63oHCv8XdstRAkEA5Lf2nA2rEi5WxvSIea5KUzQp6Ut1aCLjHpdU5Pk7 | ||
4IqCgyaC9pPvCkL6rEOthAfh9nnPJp41zMk7jHz5zmRe6g== | ||
-----END RSA PRIVATE KEY-----` | ||
|
||
var tlsConfig *tls.Config | ||
|
||
var messageRegex = regexp.MustCompile(`<133>[A-Z][a-z]{2} (([0-9]{2})|( [0-9])) [0-9]{2}:[0-9]{2}:[0-9]{2} testing\/127\.0\.0\.1 foo bar baz`) | ||
|
||
func TestNewServer(t *testing.T) { | ||
testNewServerTCP(t, false) | ||
testNewServerTCP(t, true) | ||
testNewServerUDP(t) | ||
} | ||
|
||
func testNewServerUDP(t *testing.T) { | ||
t.Logf("testing udp") | ||
serverAddr, err := net.ResolveUDPAddr("udp", host) | ||
if err != nil { | ||
t.Fatalf("could not resolve udp addr: %s", err) | ||
} | ||
conn, err := net.ListenUDP("udp", serverAddr) | ||
if err != nil { | ||
t.Fatalf("could not listen udp: %s", err) | ||
} | ||
if err := conn.SetReadDeadline(time.Now().Add(time.Second * 5)); err != nil { | ||
t.Fatalf("could not set read deadline: %s", err) | ||
} | ||
defer conn.Close() | ||
go func() { | ||
/* | ||
* Send the message 'foo bar baz' to the syslog server | ||
*/ | ||
server, err := NewServer(ConnectionUDP, host) | ||
if err != nil { | ||
t.Fatalf("could not initialize server: %s", err) | ||
} | ||
server.Hostname = "testing" // overwrite hostname for testing | ||
defer server.Close() | ||
if err := server.Send("foo bar baz", syslog.LOG_LOCAL0|syslog.LOG_NOTICE); err != nil { | ||
t.Fatalf("could not send message: %s", err) | ||
} | ||
}() | ||
buf := make([]byte, 1024) | ||
n, _, err := conn.ReadFrom(buf) | ||
if err != nil { | ||
t.Fatalf("could not read udp: %s", err) | ||
} | ||
b := buf[:n] | ||
if !messageRegex.MatchString(string(b)) { | ||
t.Fatalf("wrong message: %s", string(b)) | ||
} else { | ||
t.Logf("correct message: '%s'", string(b)) | ||
} | ||
} | ||
|
||
func testNewServerTCP(t *testing.T, useTLS bool) { | ||
t.Logf("testing tcp with tls '%s'", strconv.FormatBool(useTLS)) | ||
var ( | ||
listener net.Listener | ||
err error | ||
) | ||
if useTLS { | ||
listener, err = tls.Listen("tcp", host, tlsConfig) | ||
} else { | ||
listener, err = net.Listen("tcp", host) | ||
} | ||
if err != nil { | ||
t.Fatalf("could not listen: %s", err) | ||
} | ||
defer listener.Close() | ||
go func() { | ||
/* | ||
* Send the message 'foo bar baz' to the syslog server | ||
*/ | ||
connectionType := ConnectionTCP | ||
if useTLS { | ||
connectionType = ConnectionTLS | ||
} | ||
server, err := NewServer(connectionType, host) | ||
if err != nil { | ||
t.Fatalf("could not initialize server: %s", err) | ||
} | ||
server.Hostname = "testing" // overwrite hostname for testing | ||
defer server.Close() | ||
if err := server.Send("foo bar baz", syslog.LOG_LOCAL0|syslog.LOG_NOTICE); err != nil { | ||
t.Fatalf("could not send message: %s", err) | ||
} | ||
}() | ||
for { | ||
conn, err := listener.Accept() | ||
if err != nil { | ||
t.Fatalf("could not accept connection: %s", err) | ||
} | ||
defer conn.Close() | ||
b, err := ioutil.ReadAll(conn) | ||
if err != nil { | ||
t.Fatalf("could not read all: %s", err) | ||
} | ||
if !messageRegex.MatchString(string(b)) { | ||
t.Fatalf("wrong message: %s", string(b)) | ||
} else { | ||
t.Logf("correct message: '%s'", string(b)) | ||
} | ||
return | ||
} | ||
} | ||
|
||
func init() { | ||
pemCert, _ := pem.Decode([]byte(tlsCRT)) | ||
if pemCert == nil { | ||
panic("no test tls certificate") | ||
} | ||
pemKey, _ := pem.Decode([]byte(tlsKEY)) | ||
if pemKey == nil { | ||
panic("no test tls key") | ||
} | ||
privateKey, err := x509.ParsePKCS1PrivateKey(pemKey.Bytes) | ||
if err != nil { | ||
panic("invalid test tls key") | ||
} | ||
cert := &tls.Certificate{ | ||
Certificate: [][]byte{pemCert.Bytes}, | ||
PrivateKey: privateKey, | ||
} | ||
tlsConfig = &tls.Config{ | ||
Certificates: []tls.Certificate{*cert}, | ||
MinVersion: tls.VersionTLS11, | ||
} | ||
} |