Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added basic TLS support #120

Merged
merged 1 commit into from
May 6, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 40 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,28 @@ You can also [run it with Docker](#run-it-with-docker).

## Encryption

`irc-slack` provides no encryption between your IRC client and `irc-slack`, but
the communication between `irc-slack` and the Slack servers is encrypted.
`irc-slack` by default does not use encryption when communicating with your IRC
client (but the communication between `irc-slack` and the Slack servers is
encrypted).
If you want to use TLS, you can use the `-key` and `-cert` command line
parameters, and point them to a TLS certificate that you own.
This is useful if you plan to connect to to `irc-slack` over the internet.

For example, you can generate a valid certificate with LetsEncrypt (adjust the relevant
fields of course):
```
sudo certbot certonly \
-n \
-d your.domain.example.com \
--test-cert \
--standalone \
-m [email protected] \
--agree-tos
```

It is not recommended to connect to `irc-slack` over the internet (i.e. run it on
your loopback interface, as long as you trust your machines' users). If you need
to do so, put a TLS proxy in front of it.
Then your key and certificate will be generated under
`/etc/letsencrypt/live/your.domain.example.com`
with the names `privkey.pem` and `cert.pem` respectively.

## Authentication

Expand All @@ -47,6 +63,8 @@ three possible methods:
* legacy tokens (soon to be deprecated)

These options are discussed in more detail below.
Then just add `-key <path/to/privkey.pem> -cert <path/to/cert.pem>` to enable
TLS on `irc-slack`, and enable TLS on your IRC client.


### User tokens with auth cookie
Expand Down Expand Up @@ -130,18 +148,33 @@ docker build -f Dockerfile . -t insomniacslk/irc-slack

### Connecting with irssi
```
/network add SlackYourTeamName
/server add -auto -network SlackYourTeamName localhost 6666 xoxp-<your-slack-token>
/network add yourteam.slack.com
/server add -auto -network yourteam.slack.com localhost 6666 xoxp-<your-slack-token>
/connect yourteam.slack.com
```

Remember to add `-tls` to the `/connect` command if you're running `irc-slack`
with TLS.
Also remember to replace `localhost` with the name of the host you're connecting to,
if different.

### Connecting with WeeChat

```
/server add yourteam.slack.com localhost/6666
/set irc.server.yourteam.slack.com.password xoxp-<your-slack-token>
/connect yourteam.slack.com
```

To enable TLS, also run the following before the `/connect` command:
```
/set irc.server.yourteam.slack.com.ssl on
/set irc.server.yourteam.slack.com.ssl_verify on
```

Also remember to replace `localhost` with the name of the host you're connecting to,
if different.

## Gateway usage

There are a few options that you can pass to the server, e.g. to change the listener port, or the server name:
Expand Down
2 changes: 1 addition & 1 deletion irc_context.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ type SlackPostMessage struct {

// IrcContext holds the client context information
type IrcContext struct {
Conn *net.TCPConn
Conn net.Conn
User *slack.User
// TODO make RealName a function
RealName string
Expand Down
19 changes: 19 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package main

import (
"crypto/tls"
"flag"
"fmt"
"io/ioutil"
Expand Down Expand Up @@ -32,6 +33,8 @@ var (
logLevel = flag.String("L", "info", fmt.Sprintf("Log level. One of %v", getLogLevels()))
flagSlackDebug = flag.Bool("D", false, "Enable debug logging of the Slack API")
flagPagination = flag.Int("P", 0, "Pagination value for API calls. If 0 or unspecified, use the recommended default (currently 200). Larger values can help on large Slack teams")
flagKey = flag.String("key", "", "TLS key for HTTPS server. Requires -cert")
flagCert = flag.String("cert", "", "TLS certificate for HTTPS server. Requires -key")
)

var log = logger.GetLogger("main")
Expand Down Expand Up @@ -81,6 +84,21 @@ func main() {
log.Fatalf("Missing or invalid download directory: %s", *fileDownloadLocation)
}
}
doTLS := false
if *flagKey != "" && *flagCert != "" {
doTLS = true
}
var tlsConfig *tls.Config
if doTLS {
if *flagKey == "" || *flagCert == "" {
log.Fatalf("-key and -cert must be specified together")
}
cert, err := tls.LoadX509KeyPair(*flagCert, *flagKey)
if err != nil {
log.Fatalf("Failed to load TLS key/cert: %v", err)
}
tlsConfig = &tls.Config{Certificates: []tls.Certificate{cert}}
}
server := Server{
LocalAddr: &localAddr,
Name: sName,
Expand All @@ -89,6 +107,7 @@ func main() {
FileProxyPrefix: *fileProxyPrefix,
SlackDebug: *flagSlackDebug,
Pagination: *flagPagination,
TLSConfig: tlsConfig,
}
if err := server.Start(); err != nil {
log.Fatal(err)
Expand Down
18 changes: 12 additions & 6 deletions server.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package main

import (
"bufio"
"crypto/tls"
"fmt"
"io"
"net"
Expand All @@ -14,35 +15,40 @@ import (
type Server struct {
Name string
LocalAddr net.Addr
Listener *net.TCPListener
Listener net.Listener
SlackAPIKey string
SlackDebug bool
ChunkSize int
FileDownloadLocation string
FileProxyPrefix string
Pagination int
TLSConfig *tls.Config
}

// Start runs the IRC server
func (s Server) Start() error {
listener, err := net.Listen("tcp", s.LocalAddr.String())
var err error
if s.TLSConfig != nil {
s.Listener, err = tls.Listen("tcp", s.LocalAddr.String(), s.TLSConfig)
} else {
s.Listener, err = net.Listen("tcp", s.LocalAddr.String())
}
if err != nil {
return err
}
s.Listener = listener.(*net.TCPListener)
defer s.Listener.Close()
log.Infof("Listening on %v", s.LocalAddr)
for {
conn, err := s.Listener.Accept()
if err != nil {
return fmt.Errorf("Error accepting: %v", err)
}
go s.HandleRequest(conn.(*net.TCPConn))
go s.HandleRequest(conn)
}
}

// HandleRequest handle IRC client connections
func (s Server) HandleRequest(conn *net.TCPConn) {
func (s Server) HandleRequest(conn net.Conn) {
defer conn.Close()
reader := bufio.NewReader(conn)
for {
Expand All @@ -62,7 +68,7 @@ func (s Server) HandleRequest(conn *net.TCPConn) {
}

// HandleMsg handles raw IRC messages
func (s *Server) HandleMsg(conn *net.TCPConn, msg string) {
func (s *Server) HandleMsg(conn net.Conn, msg string) {
if strings.HasPrefix(msg, "PASS ") {
log.Debugf("%v: PASS ***** (redacted for privacy)", conn.RemoteAddr())
} else {
Expand Down