source: code/trunk/cmd/soju/main.go@ 432

Last change on this file since 432 was 418, checked in by contact, 5 years ago

Add support for the PROXY protocol

IPs whitelisted in accept-proxy-ip can now use the PROXY protocol to
indicate the original source/destination addresses.

Closes: https://todo.sr.ht/~emersion/soju/81

File size: 4.0 KB
RevLine 
[98]1package main
2
3import (
4 "crypto/tls"
5 "flag"
6 "log"
7 "net"
[323]8 "net/http"
[317]9 "net/url"
10 "strings"
[98]11
[418]12 "github.com/pires/go-proxyproto"
13
[98]14 "git.sr.ht/~emersion/soju"
15 "git.sr.ht/~emersion/soju/config"
16)
17
18func main() {
[317]19 var listen, configPath string
[98]20 var debug bool
[317]21 flag.StringVar(&listen, "listen", "", "listening address")
[98]22 flag.StringVar(&configPath, "config", "", "path to configuration file")
23 flag.BoolVar(&debug, "debug", false, "enable debug logging")
24 flag.Parse()
25
26 var cfg *config.Server
27 if configPath != "" {
28 var err error
29 cfg, err = config.Load(configPath)
30 if err != nil {
31 log.Fatalf("failed to load config file: %v", err)
32 }
33 } else {
34 cfg = config.Defaults()
35 }
36
[317]37 if listen != "" {
38 cfg.Listen = append(cfg.Listen, listen)
[98]39 }
[317]40 if len(cfg.Listen) == 0 {
41 cfg.Listen = []string{":6697"}
42 }
[98]43
44 db, err := soju.OpenSQLDB(cfg.SQLDriver, cfg.SQLSource)
45 if err != nil {
46 log.Fatalf("failed to open database: %v", err)
47 }
48
[317]49 var tlsCfg *tls.Config
[98]50 if cfg.TLS != nil {
51 cert, err := tls.LoadX509KeyPair(cfg.TLS.CertPath, cfg.TLS.KeyPath)
52 if err != nil {
53 log.Fatalf("failed to load TLS certificate and key: %v", err)
54 }
[317]55 tlsCfg = &tls.Config{Certificates: []tls.Certificate{cert}}
[98]56 }
57
58 srv := soju.NewServer(db)
59 // TODO: load from config/DB
60 srv.Hostname = cfg.Hostname
[178]61 srv.LogPath = cfg.LogPath
[323]62 srv.HTTPOrigins = cfg.HTTPOrigins
[417]63 srv.AcceptProxyIPs = cfg.AcceptProxyIPs
[98]64 srv.Debug = debug
65
[317]66 for _, listen := range cfg.Listen {
67 listenURI := listen
68 if !strings.Contains(listenURI, ":/") {
69 // This is a raw domain name, make it an URL with an empty scheme
70 listenURI = "//" + listenURI
[98]71 }
[317]72 u, err := url.Parse(listenURI)
73 if err != nil {
74 log.Fatalf("failed to parse listen URI %q: %v", listen, err)
75 }
76
77 switch u.Scheme {
78 case "ircs", "":
79 if tlsCfg == nil {
80 log.Fatalf("failed to listen on %q: missing TLS configuration", listen)
81 }
82 host := u.Host
83 if _, _, err := net.SplitHostPort(host); err != nil {
84 host = host + ":6697"
85 }
86 ln, err := tls.Listen("tcp", host, tlsCfg)
87 if err != nil {
88 log.Fatalf("failed to start TLS listener on %q: %v", listen, err)
89 }
[418]90 ln = proxyProtoListener(ln, srv)
[317]91 go func() {
92 log.Fatal(srv.Serve(ln))
93 }()
94 case "irc+insecure":
95 host := u.Host
96 if _, _, err := net.SplitHostPort(host); err != nil {
97 host = host + ":6667"
98 }
99 ln, err := net.Listen("tcp", host)
100 if err != nil {
101 log.Fatalf("failed to start listener on %q: %v", listen, err)
102 }
[418]103 ln = proxyProtoListener(ln, srv)
[317]104 go func() {
105 log.Fatal(srv.Serve(ln))
106 }()
[323]107 case "wss":
108 addr := u.Host
109 if _, _, err := net.SplitHostPort(addr); err != nil {
110 addr = addr + ":https"
111 }
112 httpSrv := http.Server{
113 Addr: addr,
114 TLSConfig: tlsCfg,
115 Handler: srv,
116 }
117 go func() {
118 log.Fatal(httpSrv.ListenAndServeTLS("", ""))
119 }()
120 case "ws+insecure":
121 addr := u.Host
122 if _, _, err := net.SplitHostPort(addr); err != nil {
123 addr = addr + ":http"
124 }
125 httpSrv := http.Server{
126 Addr: addr,
127 Handler: srv,
128 }
129 go func() {
130 log.Fatal(httpSrv.ListenAndServe())
131 }()
[385]132 case "ident":
133 if srv.Identd == nil {
134 srv.Identd = soju.NewIdentd()
135 }
136
137 host := u.Host
138 if _, _, err := net.SplitHostPort(host); err != nil {
139 host = host + ":113"
140 }
141 ln, err := net.Listen("tcp", host)
142 if err != nil {
143 log.Fatalf("failed to start listener on %q: %v", listen, err)
144 }
[418]145 ln = proxyProtoListener(ln, srv)
[385]146 go func() {
147 log.Fatal(srv.Identd.Serve(ln))
148 }()
[317]149 default:
150 log.Fatalf("failed to listen on %q: unsupported scheme", listen)
151 }
152
153 log.Printf("server listening on %q", listen)
154 }
155 log.Fatal(srv.Run())
[98]156}
[418]157
158func proxyProtoListener(ln net.Listener, srv *soju.Server) net.Listener {
159 return &proxyproto.Listener{
160 Listener: ln,
161 Policy: func(upstream net.Addr) (proxyproto.Policy, error) {
162 tcpAddr, ok := upstream.(*net.TCPAddr)
163 if !ok {
164 return proxyproto.IGNORE, nil
165 }
166 if srv.AcceptProxyIPs.Contains(tcpAddr.IP) {
167 return proxyproto.USE, nil
168 }
169 return proxyproto.IGNORE, nil
170 },
171 }
172}
Note: See TracBrowser for help on using the repository browser.