config.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438
  1. package guerrilla
  2. import (
  3. "crypto/tls"
  4. "encoding/json"
  5. "errors"
  6. "fmt"
  7. "os"
  8. "reflect"
  9. "strings"
  10. "github.com/flashmob/go-guerrilla/backends"
  11. "github.com/flashmob/go-guerrilla/dashboard"
  12. "github.com/flashmob/go-guerrilla/log"
  13. )
  14. // AppConfig is the holder of the configuration of the app
  15. type AppConfig struct {
  16. // Servers can have one or more items.
  17. /// Defaults to 1 server listening on 127.0.0.1:2525
  18. Servers []ServerConfig `json:"servers"`
  19. // AllowedHosts lists which hosts to accept email for. Defaults to os.Hostname
  20. AllowedHosts []string `json:"allowed_hosts"`
  21. // PidFile is the path for writing out the process id. No output if empty
  22. PidFile string `json:"pid_file"`
  23. // LogFile is where the logs go. Use path to file, or "stderr", "stdout"
  24. // or "off". Default "stderr"
  25. LogFile string `json:"log_file,omitempty"`
  26. // LogLevel controls the lowest level we log.
  27. // "info", "debug", "error", "panic". Default "info"
  28. LogLevel string `json:"log_level,omitempty"`
  29. // BackendConfig configures the email envelope processing backend
  30. BackendConfig backends.BackendConfig `json:"backend_config"`
  31. // Dashboard config configures how analytics are gathered and displayed
  32. Dashboard dashboard.Config `json:"dashboard"`
  33. }
  34. // ServerConfig specifies config options for a single server
  35. type ServerConfig struct {
  36. // IsEnabled set to true to start the server, false will ignore it
  37. IsEnabled bool `json:"is_enabled"`
  38. // Hostname will be used in the server's reply to HELO/EHLO. If TLS enabled
  39. // make sure that the Hostname matches the cert. Defaults to os.Hostname()
  40. Hostname string `json:"host_name"`
  41. // MaxSize is the maximum size of an email that will be accepted for delivery.
  42. // Defaults to 10 Mebibytes
  43. MaxSize int64 `json:"max_size"`
  44. // PrivateKeyFile path to cert private key in PEM format. Will be ignored if blank
  45. PrivateKeyFile string `json:"private_key_file"`
  46. // PublicKeyFile path to cert (public key) chain in PEM format.
  47. // Will be ignored if blank
  48. PublicKeyFile string `json:"public_key_file"`
  49. // Timeout specifies the connection timeout in seconds. Defaults to 30
  50. Timeout int `json:"timeout"`
  51. // Listen interface specified in <ip>:<port> - defaults to 127.0.0.1:2525
  52. ListenInterface string `json:"listen_interface"`
  53. // StartTLSOn should we offer STARTTLS command. Cert must be valid.
  54. // False by default
  55. StartTLSOn bool `json:"start_tls_on,omitempty"`
  56. // TLSAlwaysOn run this server as a pure TLS server, i.e. SMTPS
  57. TLSAlwaysOn bool `json:"tls_always_on,omitempty"`
  58. // MaxClients controls how many maxiumum clients we can handle at once.
  59. // Defaults to 100
  60. MaxClients int `json:"max_clients"`
  61. // LogFile is where the logs go. Use path to file, or "stderr", "stdout" or "off".
  62. // defaults to AppConfig.Log file setting
  63. LogFile string `json:"log_file,omitempty"`
  64. // XClientOn when using a proxy such as Nginx, XCLIENT command is used to pass the
  65. // original client's IP address & client's HELO
  66. XClientOn bool `json:"xclient_on,omitempty"`
  67. // The following used to watch certificate changes so that the TLS can be reloaded
  68. _privateKeyFile_mtime int
  69. _publicKeyFile_mtime int
  70. }
  71. // Unmarshalls json data into AppConfig struct and any other initialization of the struct
  72. // also does validation, returns error if validation failed or something went wrong
  73. func (c *AppConfig) Load(jsonBytes []byte) error {
  74. err := json.Unmarshal(jsonBytes, c)
  75. if err != nil {
  76. return fmt.Errorf("could not parse config file: %s", err)
  77. }
  78. if err = c.setDefaults(); err != nil {
  79. return err
  80. }
  81. if err = c.setBackendDefaults(); err != nil {
  82. return err
  83. }
  84. // all servers must be valid in order to continue
  85. for _, server := range c.Servers {
  86. if errs := server.Validate(); errs != nil {
  87. return errs
  88. }
  89. }
  90. // read the timestamps for the ssl keys, to determine if they need to be reloaded
  91. for i := 0; i < len(c.Servers); i++ {
  92. c.Servers[i].loadTlsKeyTimestamps()
  93. }
  94. return nil
  95. }
  96. // Emits any configuration change events onto the event bus.
  97. func (c *AppConfig) EmitChangeEvents(oldConfig *AppConfig, app Guerrilla) {
  98. // has backend changed?
  99. if !reflect.DeepEqual((*c).BackendConfig, (*oldConfig).BackendConfig) {
  100. app.Publish(EventConfigBackendConfig, c)
  101. }
  102. // has config changed, general check
  103. if !reflect.DeepEqual(oldConfig, c) {
  104. app.Publish(EventConfigNewConfig, c)
  105. }
  106. // has 'allowed hosts' changed?
  107. if !reflect.DeepEqual(oldConfig.AllowedHosts, c.AllowedHosts) {
  108. app.Publish(EventConfigAllowedHosts, c)
  109. }
  110. // has pid file changed?
  111. if strings.Compare(oldConfig.PidFile, c.PidFile) != 0 {
  112. app.Publish(EventConfigPidFile, c)
  113. }
  114. // has mainlog log changed?
  115. if strings.Compare(oldConfig.LogFile, c.LogFile) != 0 {
  116. app.Publish(EventConfigLogFile, c)
  117. }
  118. // has log level changed?
  119. if strings.Compare(oldConfig.LogLevel, c.LogLevel) != 0 {
  120. app.Publish(EventConfigLogLevel, c)
  121. }
  122. // server config changes
  123. oldServers := oldConfig.getServers()
  124. for iface, newServer := range c.getServers() {
  125. // is server is in both configs?
  126. if oldServer, ok := oldServers[iface]; ok {
  127. // since old server exists in the new config, we do not track it anymore
  128. delete(oldServers, iface)
  129. // so we know the server exists in both old & new configs
  130. newServer.emitChangeEvents(oldServer, app)
  131. } else {
  132. // start new server
  133. app.Publish(EventConfigServerNew, newServer)
  134. }
  135. }
  136. // remove any servers that don't exist anymore
  137. for _, oldserver := range oldServers {
  138. app.Publish(EventConfigServerRemove, oldserver)
  139. }
  140. }
  141. // EmitLogReopen emits log reopen events using existing config
  142. func (c *AppConfig) EmitLogReopenEvents(app Guerrilla) {
  143. app.Publish(EventConfigLogReopen, c)
  144. for _, sc := range c.getServers() {
  145. app.Publish(EventConfigServerLogReopen, sc)
  146. }
  147. }
  148. // gets the servers in a map (key by interface) for easy lookup
  149. func (c *AppConfig) getServers() map[string]*ServerConfig {
  150. servers := make(map[string]*ServerConfig, len(c.Servers))
  151. for i := 0; i < len(c.Servers); i++ {
  152. servers[c.Servers[i].ListenInterface] = &c.Servers[i]
  153. }
  154. return servers
  155. }
  156. // setDefaults fills in default server settings for values that were not configured
  157. // The defaults are:
  158. // * Server listening to 127.0.0.1:2525
  159. // * use your hostname to determine your which hosts to accept email for
  160. // * 100 maximum clients
  161. // * 10MB max message size
  162. // * log to Stderr,
  163. // * log level set to "`debug`"
  164. // * timeout to 30 sec
  165. // * Backend configured with the following processors: `HeadersParser|Header|Debugger`
  166. // where it will log the received emails.
  167. func (c *AppConfig) setDefaults() error {
  168. if c.LogFile == "" {
  169. c.LogFile = log.OutputStderr.String()
  170. }
  171. if c.LogLevel == "" {
  172. c.LogLevel = "debug"
  173. }
  174. if len(c.AllowedHosts) == 0 {
  175. if h, err := os.Hostname(); err != nil {
  176. return err
  177. } else {
  178. c.AllowedHosts = append(c.AllowedHosts, h)
  179. }
  180. }
  181. h, err := os.Hostname()
  182. if err != nil {
  183. return err
  184. }
  185. if len(c.Servers) == 0 {
  186. sc := ServerConfig{}
  187. sc.LogFile = c.LogFile
  188. sc.ListenInterface = defaultInterface
  189. sc.IsEnabled = true
  190. sc.Hostname = h
  191. sc.MaxClients = 100
  192. sc.Timeout = 30
  193. sc.MaxSize = 10 << 20 // 10 Mebibytes
  194. c.Servers = append(c.Servers, sc)
  195. } else {
  196. // make sure each server has defaults correctly configured
  197. for i := range c.Servers {
  198. if c.Servers[i].Hostname == "" {
  199. c.Servers[i].Hostname = h
  200. }
  201. if c.Servers[i].MaxClients == 0 {
  202. c.Servers[i].MaxClients = 100
  203. }
  204. if c.Servers[i].Timeout == 0 {
  205. c.Servers[i].Timeout = 20
  206. }
  207. if c.Servers[i].MaxSize == 0 {
  208. c.Servers[i].MaxSize = 10 << 20 // 10 Mebibytes
  209. }
  210. if c.Servers[i].ListenInterface == "" {
  211. return errors.New(fmt.Sprintf("Listen interface not specified for server at index %d", i))
  212. }
  213. if c.Servers[i].LogFile == "" {
  214. c.Servers[i].LogFile = c.LogFile
  215. }
  216. // validate the server config
  217. err = c.Servers[i].Validate()
  218. if err != nil {
  219. return err
  220. }
  221. }
  222. }
  223. return nil
  224. }
  225. // setBackendDefaults sets default values for the backend config,
  226. // if no backend config was added before starting, then use a default config
  227. // otherwise, see what required values were missed in the config and add any missing with defaults
  228. func (c *AppConfig) setBackendDefaults() error {
  229. if len(c.BackendConfig) == 0 {
  230. h, err := os.Hostname()
  231. if err != nil {
  232. return err
  233. }
  234. c.BackendConfig = backends.BackendConfig{
  235. "log_received_mails": true,
  236. "save_workers_size": 1,
  237. "save_process": "HeadersParser|Header|Debugger",
  238. "primary_mail_host": h,
  239. }
  240. } else {
  241. if _, ok := c.BackendConfig["save_process"]; !ok {
  242. c.BackendConfig["save_process"] = "HeadersParser|Header|Debugger"
  243. }
  244. if _, ok := c.BackendConfig["primary_mail_host"]; !ok {
  245. h, err := os.Hostname()
  246. if err != nil {
  247. return err
  248. }
  249. c.BackendConfig["primary_mail_host"] = h
  250. }
  251. if _, ok := c.BackendConfig["save_workers_size"]; !ok {
  252. c.BackendConfig["save_workers_size"] = 1
  253. }
  254. if _, ok := c.BackendConfig["log_received_mails"]; !ok {
  255. c.BackendConfig["log_received_mails"] = false
  256. }
  257. }
  258. return nil
  259. }
  260. // Emits any configuration change events on the server.
  261. // All events are fired and run synchronously
  262. func (sc *ServerConfig) emitChangeEvents(oldServer *ServerConfig, app Guerrilla) {
  263. // get a list of changes
  264. changes := getDiff(
  265. *oldServer,
  266. *sc,
  267. )
  268. if len(changes) > 0 {
  269. // something changed in the server config
  270. app.Publish(EventConfigServerConfig, sc)
  271. }
  272. // enable or disable?
  273. if _, ok := changes["IsEnabled"]; ok {
  274. if sc.IsEnabled {
  275. app.Publish(EventConfigServerStart, sc)
  276. } else {
  277. app.Publish(EventConfigServerStop, sc)
  278. }
  279. // do not emit any more events when IsEnabled changed
  280. return
  281. }
  282. // log file change?
  283. if _, ok := changes["LogFile"]; ok {
  284. app.Publish(EventConfigServerLogFile, sc)
  285. } else {
  286. // since config file has not changed, we reload it
  287. app.Publish(EventConfigServerLogReopen, sc)
  288. }
  289. // timeout changed
  290. if _, ok := changes["Timeout"]; ok {
  291. app.Publish(EventConfigServerTimeout, sc)
  292. }
  293. // max_clients changed
  294. if _, ok := changes["MaxClients"]; ok {
  295. app.Publish(EventConfigServerMaxClients, sc)
  296. }
  297. // tls changed
  298. if ok := func() bool {
  299. if _, ok := changes["PrivateKeyFile"]; ok {
  300. return true
  301. }
  302. if _, ok := changes["PublicKeyFile"]; ok {
  303. return true
  304. }
  305. if _, ok := changes["StartTLSOn"]; ok {
  306. return true
  307. }
  308. if _, ok := changes["TLSAlwaysOn"]; ok {
  309. return true
  310. }
  311. return false
  312. }(); ok {
  313. app.Publish(EventConfigServerTLSConfig, sc)
  314. }
  315. }
  316. // Loads in timestamps for the ssl keys
  317. func (sc *ServerConfig) loadTlsKeyTimestamps() error {
  318. var statErr = func(iface string, err error) error {
  319. return errors.New(
  320. fmt.Sprintf(
  321. "could not stat key for server [%s], %s",
  322. iface,
  323. err.Error()))
  324. }
  325. if info, err := os.Stat(sc.PrivateKeyFile); err == nil {
  326. sc._privateKeyFile_mtime = info.ModTime().Second()
  327. } else {
  328. return statErr(sc.ListenInterface, err)
  329. }
  330. if info, err := os.Stat(sc.PublicKeyFile); err == nil {
  331. sc._publicKeyFile_mtime = info.ModTime().Second()
  332. } else {
  333. return statErr(sc.ListenInterface, err)
  334. }
  335. return nil
  336. }
  337. // Gets the timestamp of the TLS certificates. Returns a unix time of when they were last modified
  338. // when the config was read. We use this info to determine if TLS needs to be re-loaded.
  339. func (sc *ServerConfig) getTlsKeyTimestamps() (int, int) {
  340. return sc._privateKeyFile_mtime, sc._publicKeyFile_mtime
  341. }
  342. // Validate validates the server's configuration.
  343. func (sc *ServerConfig) Validate() error {
  344. var errs Errors
  345. if sc.StartTLSOn || sc.TLSAlwaysOn {
  346. if sc.PublicKeyFile == "" {
  347. errs = append(errs, errors.New("PublicKeyFile is empty"))
  348. }
  349. if sc.PrivateKeyFile == "" {
  350. errs = append(errs, errors.New("PrivateKeyFile is empty"))
  351. }
  352. if _, err := tls.LoadX509KeyPair(sc.PublicKeyFile, sc.PrivateKeyFile); err != nil {
  353. errs = append(errs,
  354. errors.New(fmt.Sprintf("cannot use TLS config for [%s], %v", sc.ListenInterface, err)))
  355. }
  356. }
  357. if len(errs) > 0 {
  358. return errs
  359. }
  360. return nil
  361. }
  362. // Returns a diff between struct a & struct b.
  363. // Results are returned in a map, where each key is the name of the field that was different.
  364. // a and b are struct values, must not be pointer
  365. // and of the same struct type
  366. func getDiff(a interface{}, b interface{}) map[string]interface{} {
  367. ret := make(map[string]interface{}, 5)
  368. compareWith := structtomap(b)
  369. for key, val := range structtomap(a) {
  370. if val != compareWith[key] {
  371. ret[key] = compareWith[key]
  372. }
  373. }
  374. // detect tls changes (have the key files been modified?)
  375. if oldServer, ok := a.(ServerConfig); ok {
  376. t1, t2 := oldServer.getTlsKeyTimestamps()
  377. if newServer, ok := b.(ServerConfig); ok {
  378. t3, t4 := newServer.getTlsKeyTimestamps()
  379. if t1 != t3 {
  380. ret["PrivateKeyFile"] = newServer.PrivateKeyFile
  381. }
  382. if t2 != t4 {
  383. ret["PublicKeyFile"] = newServer.PublicKeyFile
  384. }
  385. }
  386. }
  387. return ret
  388. }
  389. // Convert fields of a struct to a map
  390. // only able to convert int, bool and string; not recursive
  391. func structtomap(obj interface{}) map[string]interface{} {
  392. ret := make(map[string]interface{}, 0)
  393. v := reflect.ValueOf(obj)
  394. t := v.Type()
  395. for index := 0; index < v.NumField(); index++ {
  396. vField := v.Field(index)
  397. fName := t.Field(index).Name
  398. switch vField.Kind() {
  399. case reflect.Int:
  400. value := vField.Int()
  401. ret[fName] = value
  402. case reflect.String:
  403. value := vField.String()
  404. ret[fName] = value
  405. case reflect.Bool:
  406. value := vField.Bool()
  407. ret[fName] = value
  408. }
  409. }
  410. return ret
  411. }