diff --git a/config.conf.example b/config.conf.example index 7a69082..0db147f 100644 --- a/config.conf.example +++ b/config.conf.example @@ -10,6 +10,10 @@ gateway_name = "webircgateway" # A secret string used for generating client JWT tokens. Do not share this! secret = "" +# Webirc client certificate sent to servers when connecting with TLS +webirc_cert = "" +webirc_key = "" + # Send the server a quit message when the client is closed # Comment out to disable send_quit_on_client_close = "Client closed" diff --git a/pkg/proxy/proxy.go b/pkg/proxy/proxy.go index c332f40..1f2a4d7 100644 --- a/pkg/proxy/proxy.go +++ b/pkg/proxy/proxy.go @@ -31,6 +31,8 @@ type KiwiProxyConnection struct { DestTLS bool State KiwiProxyState Conn *net.Conn + WebircPemCert []byte + WebircPemKey []byte } func MakeKiwiProxyConnection() *KiwiProxyConnection { @@ -63,11 +65,13 @@ func (c *KiwiProxyConnection) Dial(proxyServerAddr string) error { c.State = KiwiProxyStateHandshaking meta, _ := json.Marshal(map[string]interface{}{ - "username": c.Username, - "interface": c.ProxyInterface, - "host": c.DestHost, - "port": c.DestPort, - "ssl": c.DestTLS, + "username": c.Username, + "interface": c.ProxyInterface, + "host": c.DestHost, + "port": c.DestPort, + "ssl": c.DestTLS, + "webirc_cert": c.WebircPemCert, + "webirc_key": c.WebircPemKey, }) (*c.Conn).Write(append(meta, byte('\n'))) diff --git a/pkg/proxy/server.go b/pkg/proxy/server.go index 7e3f62f..7452d9e 100644 --- a/pkg/proxy/server.go +++ b/pkg/proxy/server.go @@ -29,11 +29,13 @@ var identdRpc *identd.RpcClient var Server net.Listener type HandshakeMeta struct { - Host string `json:"host"` - Port int `json:"port"` - TLS bool `json:"ssl"` - Username string `json:"username"` - Interface string `json:"interface"` + Host string `json:"host"` + Port int `json:"port"` + TLS bool `json:"ssl"` + Username string `json:"username"` + Interface string `json:"interface"` + WebircPemCert []byte `json:"webirc_cert"` + WebircPemKey []byte `json:"webirc_key"` } func MakeClient(conn net.Conn) *Client { @@ -43,12 +45,13 @@ func MakeClient(conn net.Conn) *Client { } type Client struct { - Client net.Conn - Upstream net.Conn - UpstreamAddr *net.TCPAddr - Username string - BindAddr *net.TCPAddr - TLS bool + Client net.Conn + Upstream net.Conn + UpstreamAddr *net.TCPAddr + Username string + BindAddr *net.TCPAddr + TLS bool + WebircCertificate []tls.Certificate } func (c *Client) Run() { @@ -88,6 +91,13 @@ func (c *Client) Handshake() error { return unmarshalErr } + if len(meta.WebircPemCert) > 0 && len(meta.WebircPemKey) > 0 { + webircCert, err := tls.X509KeyPair(meta.WebircPemCert, meta.WebircPemKey) + if err == nil { + c.WebircCertificate = []tls.Certificate{webircCert} + } + } + if meta.Host == "" || meta.Port == 0 || meta.Username == "" || meta.Interface == "" { c.Client.Write([]byte(ResponseError)) return fmt.Errorf("missing args") @@ -143,7 +153,10 @@ func (c *Client) ConnectUpstream() error { } if c.TLS { - tlsConfig := &tls.Config{InsecureSkipVerify: true} + tlsConfig := &tls.Config{ + InsecureSkipVerify: true, + Certificates: c.WebircCertificate, + } tlsConn := tls.Client(conn, tlsConfig) err := tlsConn.Handshake() if err != nil { diff --git a/pkg/webircgateway/client.go b/pkg/webircgateway/client.go index e0e9d1d..3776b26 100644 --- a/pkg/webircgateway/client.go +++ b/pkg/webircgateway/client.go @@ -303,6 +303,15 @@ func (c *Client) makeUpstreamConnection() (io.ReadWriteCloser, error) { client := c upstreamConfig := c.UpstreamConfig + // TODO remove me + upstreamConfig.Proxy = &ConfigProxy{ + Type: "kiwi", + Hostname: "127.0.0.1", + Port: 7999, + TLS: false, + Username: client.IrcState.Username, + Interface: "0.0.0.0", + } var connection io.ReadWriteCloser if upstreamConfig.Proxy == nil { @@ -343,7 +352,10 @@ func (c *Client) makeUpstreamConnection() (io.ReadWriteCloser, error) { } if upstreamConfig.TLS { - tlsConfig := &tls.Config{InsecureSkipVerify: true} + tlsConfig := &tls.Config{ + InsecureSkipVerify: true, + Certificates: upstreamConfig.WebircCertificate, + } tlsConn := tls.Client(conn, tlsConfig) err := tlsConn.Handshake() if err != nil { @@ -367,6 +379,8 @@ func (c *Client) makeUpstreamConnection() (io.ReadWriteCloser, error) { conn.DestTLS = upstreamConfig.TLS conn.Username = upstreamConfig.Proxy.Username conn.ProxyInterface = upstreamConfig.Proxy.Interface + conn.WebircPemCert = upstreamConfig.WebircPemCert + conn.WebircPemKey = upstreamConfig.WebircPemKey dialErr := conn.Dial(fmt.Sprintf( "%s:%d", @@ -401,6 +415,10 @@ func (c *Client) makeUpstreamConnection() (io.ReadWriteCloser, error) { func (c *Client) writeWebircLines(upstream io.ReadWriteCloser) { // Send any WEBIRC lines + if len(c.UpstreamConfig.WebircCertificate) > 0 && c.UpstreamConfig.WebircPassword == "" { + c.UpstreamConfig.WebircPassword = "*" + } + if c.UpstreamConfig.WebircPassword == "" { c.Log(1, "No webirc to send") return @@ -696,7 +714,14 @@ func (c *Client) configureUpstream() ConfigUpstream { upstreamConfig.Timeout = c.Gateway.Config.GatewayTimeout upstreamConfig.Throttle = c.Gateway.Config.GatewayThrottle upstreamConfig.WebircPassword = c.Gateway.findWebircPassword(c.DestHost) + upstreamConfig.WebircPemCert = c.Gateway.Config.WebircPemCert + upstreamConfig.WebircPemKey = c.Gateway.Config.WebircPemKey + if c.Gateway.Config.WebircCertificate.Certificate != nil { + upstreamConfig.WebircCertificate = []tls.Certificate{ + *c.Gateway.Config.WebircCertificate, + } + } return upstreamConfig } diff --git a/pkg/webircgateway/config.go b/pkg/webircgateway/config.go index 8fe8a34..865a212 100644 --- a/pkg/webircgateway/config.go +++ b/pkg/webircgateway/config.go @@ -1,7 +1,9 @@ package webircgateway import ( + "crypto/tls" "errors" + "io/ioutil" "net" "os" "os/exec" @@ -27,6 +29,9 @@ type ConfigUpstream struct { ServerPassword string GatewayName string Proxy *ConfigProxy + WebircCertificate []tls.Certificate + WebircPemCert []byte + WebircPemKey []byte } // ConfigServer - A web server config @@ -77,6 +82,9 @@ type Config struct { ReCaptchaSecret string ReCaptchaKey string Secret string + WebircCertificate *tls.Certificate + WebircPemCert []byte + WebircPemKey []byte Plugins []string DnsblServers []string // DnsblAction - "deny" = deny the connection. "verify" = require verification @@ -148,6 +156,9 @@ func (c *Config) Load() error { c.ReCaptchaKey = "" c.RequiresVerification = false c.Secret = "" + c.WebircCertificate = nil + c.WebircPemCert = make([]byte, 0) + c.WebircPemKey = make([]byte, 0) c.SendQuitOnClientClose = "" c.ClientRealname = "" c.ClientUsername = "" @@ -172,6 +183,33 @@ func (c *Config) Load() error { } c.Secret = section.Key("secret").MustString("") + + // Load webirc client certificate + webircCert := section.Key("webirc_cert").MustString("") + webircKey := section.Key("webirc_key").MustString("") + if webircCert != "" && webircKey != "" { + certPath := c.ResolvePath(webircCert) + keyPath := c.ResolvePath(webircKey) + + c.WebircPemCert, err = ioutil.ReadFile(certPath) + if err != nil { + c.gateway.Log(3, "Failed to load webirc certificate, "+err.Error()) + continue + } + + c.WebircPemKey, err = ioutil.ReadFile(keyPath) + if err != nil { + c.gateway.Log(3, "Failed to load webirc certificate, "+err.Error()) + continue + } + + webircCert, err := tls.X509KeyPair(c.WebircPemCert, c.WebircPemKey) + if err == nil { + c.WebircCertificate = &webircCert + } else { + c.gateway.Log(3, "Failed to load webirc certificate, "+err.Error()) + } + } c.SendQuitOnClientClose = section.Key("send_quit_on_client_close").MustString("Connection closed") }