Nevar pievienot vairāk kā 25 tēmas Tēmai ir jāsākas ar burtu vai ciparu, tā var saturēt domu zīmes ('-') un var būt līdz 35 simboliem gara.

config.go 16KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557
  1. // Copyright (c) 2012-2014 Jeremy Latt
  2. // Copyright (c) 2014-2015 Edmund Huber
  3. // Copyright (c) 2016-2017 Daniel Oaks <daniel@danieloaks.net>
  4. // released under the MIT license
  5. package irc
  6. import (
  7. "crypto/tls"
  8. "errors"
  9. "fmt"
  10. "io/ioutil"
  11. "log"
  12. "path/filepath"
  13. "strings"
  14. "time"
  15. "code.cloudfoundry.org/bytefmt"
  16. "github.com/oragono/oragono/irc/connection_limits"
  17. "github.com/oragono/oragono/irc/custime"
  18. "github.com/oragono/oragono/irc/logger"
  19. "github.com/oragono/oragono/irc/passwd"
  20. "github.com/oragono/oragono/irc/utils"
  21. "gopkg.in/yaml.v2"
  22. )
  23. // PassConfig holds the connection password.
  24. type PassConfig struct {
  25. Password string
  26. }
  27. // TLSListenConfig defines configuration options for listening on TLS.
  28. type TLSListenConfig struct {
  29. Cert string
  30. Key string
  31. }
  32. // Config returns the TLS contiguration assicated with this TLSListenConfig.
  33. func (conf *TLSListenConfig) Config() (*tls.Config, error) {
  34. cert, err := tls.LoadX509KeyPair(conf.Cert, conf.Key)
  35. if err != nil {
  36. return nil, errors.New("tls cert+key: invalid pair")
  37. }
  38. return &tls.Config{
  39. Certificates: []tls.Certificate{cert},
  40. }, err
  41. }
  42. // PasswordBytes returns the bytes represented by the password hash.
  43. func (conf *PassConfig) PasswordBytes() []byte {
  44. bytes, err := passwd.DecodePasswordHash(conf.Password)
  45. if err != nil {
  46. log.Fatal("decode password error: ", err)
  47. }
  48. return bytes
  49. }
  50. // AccountRegistrationConfig controls account registration.
  51. type AccountRegistrationConfig struct {
  52. Enabled bool
  53. EnabledCallbacks []string `yaml:"enabled-callbacks"`
  54. Callbacks struct {
  55. Mailto struct {
  56. Server string
  57. Port int
  58. TLS struct {
  59. Enabled bool
  60. InsecureSkipVerify bool `yaml:"insecure_skip_verify"`
  61. ServerName string `yaml:"servername"`
  62. }
  63. Username string
  64. Password string
  65. Sender string
  66. VerifyMessageSubject string `yaml:"verify-message-subject"`
  67. VerifyMessage string `yaml:"verify-message"`
  68. }
  69. }
  70. AllowMultiplePerConnection bool `yaml:"allow-multiple-per-connection"`
  71. }
  72. // ChannelRegistrationConfig controls channel registration.
  73. type ChannelRegistrationConfig struct {
  74. Enabled bool
  75. }
  76. // OperClassConfig defines a specific operator class.
  77. type OperClassConfig struct {
  78. Title string
  79. WhoisLine string
  80. Extends string
  81. Capabilities []string
  82. }
  83. // OperConfig defines a specific operator's configuration.
  84. type OperConfig struct {
  85. Class string
  86. Vhost string
  87. WhoisLine string `yaml:"whois-line"`
  88. Password string
  89. Modes string
  90. }
  91. // PasswordBytes returns the bytes represented by the password hash.
  92. func (conf *OperConfig) PasswordBytes() []byte {
  93. bytes, err := passwd.DecodePasswordHash(conf.Password)
  94. if err != nil {
  95. log.Fatal("decode password error: ", err)
  96. }
  97. return bytes
  98. }
  99. // LineLenConfig controls line lengths.
  100. type LineLenConfig struct {
  101. Tags int
  102. Rest int
  103. }
  104. // STSConfig controls the STS configuration/
  105. type STSConfig struct {
  106. Enabled bool
  107. Duration time.Duration `yaml:"duration-real"`
  108. DurationString string `yaml:"duration"`
  109. Port int
  110. Preload bool
  111. }
  112. // Value returns the STS value to advertise in CAP
  113. func (sts *STSConfig) Value() string {
  114. val := fmt.Sprintf("duration=%d,", int(sts.Duration.Seconds()))
  115. if sts.Enabled && sts.Port > 0 {
  116. val += fmt.Sprintf(",port=%d", sts.Port)
  117. }
  118. if sts.Enabled && sts.Preload {
  119. val += ",preload"
  120. }
  121. return val
  122. }
  123. // StackImpactConfig is the config used for StackImpact's profiling.
  124. type StackImpactConfig struct {
  125. Enabled bool
  126. AgentKey string `yaml:"agent-key"`
  127. AppName string `yaml:"app-name"`
  128. }
  129. // LangData is the data contained in a language file.
  130. type LangData struct {
  131. Name string
  132. Code string
  133. Maintainers string
  134. Incomplete bool
  135. Translations map[string]string
  136. }
  137. // Config defines the overall configuration.
  138. type Config struct {
  139. Network struct {
  140. Name string
  141. }
  142. Server struct {
  143. PassConfig
  144. Password string
  145. Name string
  146. Listen []string
  147. TLSListeners map[string]*TLSListenConfig `yaml:"tls-listeners"`
  148. STS STSConfig
  149. CheckIdent bool `yaml:"check-ident"`
  150. MOTD string
  151. MOTDFormatting bool `yaml:"motd-formatting"`
  152. ProxyAllowedFrom []string `yaml:"proxy-allowed-from"`
  153. WebIRC []webircConfig `yaml:"webirc"`
  154. MaxSendQString string `yaml:"max-sendq"`
  155. MaxSendQBytes uint64
  156. ConnectionLimiter connection_limits.LimiterConfig `yaml:"connection-limits"`
  157. ConnectionThrottler connection_limits.ThrottlerConfig `yaml:"connection-throttling"`
  158. }
  159. Languages struct {
  160. Enabled bool
  161. Path string
  162. Default string
  163. Data map[string]LangData
  164. }
  165. Datastore struct {
  166. Path string
  167. }
  168. Accounts struct {
  169. Registration AccountRegistrationConfig
  170. AuthenticationEnabled bool `yaml:"authentication-enabled"`
  171. }
  172. Channels struct {
  173. DefaultModes *string `yaml:"default-modes"`
  174. Registration ChannelRegistrationConfig
  175. }
  176. OperClasses map[string]*OperClassConfig `yaml:"oper-classes"`
  177. Opers map[string]*OperConfig
  178. Logging []logger.LoggingConfig
  179. Debug struct {
  180. RecoverFromErrors *bool `yaml:"recover-from-errors"`
  181. StackImpact StackImpactConfig
  182. }
  183. Limits struct {
  184. AwayLen uint `yaml:"awaylen"`
  185. ChanListModes uint `yaml:"chan-list-modes"`
  186. ChannelLen uint `yaml:"channellen"`
  187. KickLen uint `yaml:"kicklen"`
  188. MonitorEntries uint `yaml:"monitor-entries"`
  189. NickLen uint `yaml:"nicklen"`
  190. TopicLen uint `yaml:"topiclen"`
  191. WhowasEntries uint `yaml:"whowas-entries"`
  192. LineLen LineLenConfig `yaml:"linelen"`
  193. }
  194. Filename string
  195. }
  196. // OperClass defines an assembled operator class.
  197. type OperClass struct {
  198. Title string
  199. WhoisLine string `yaml:"whois-line"`
  200. Capabilities map[string]bool // map to make lookups much easier
  201. }
  202. // OperatorClasses returns a map of assembled operator classes from the given config.
  203. func (conf *Config) OperatorClasses() (*map[string]OperClass, error) {
  204. ocs := make(map[string]OperClass)
  205. // loop from no extends to most extended, breaking if we can't add any more
  206. lenOfLastOcs := -1
  207. for {
  208. if lenOfLastOcs == len(ocs) {
  209. return nil, errors.New("OperClasses contains a looping dependency, or a class extends from a class that doesn't exist")
  210. }
  211. lenOfLastOcs = len(ocs)
  212. var anyMissing bool
  213. for name, info := range conf.OperClasses {
  214. _, exists := ocs[name]
  215. _, extendsExists := ocs[info.Extends]
  216. if exists {
  217. // class already exists
  218. continue
  219. } else if len(info.Extends) > 0 && !extendsExists {
  220. // class we extend on doesn't exist
  221. _, exists := conf.OperClasses[info.Extends]
  222. if !exists {
  223. return nil, fmt.Errorf("Operclass [%s] extends [%s], which doesn't exist", name, info.Extends)
  224. }
  225. anyMissing = true
  226. continue
  227. }
  228. // create new operclass
  229. var oc OperClass
  230. oc.Capabilities = make(map[string]bool)
  231. // get inhereted info from other operclasses
  232. if len(info.Extends) > 0 {
  233. einfo, _ := ocs[info.Extends]
  234. for capab := range einfo.Capabilities {
  235. oc.Capabilities[capab] = true
  236. }
  237. }
  238. // add our own info
  239. oc.Title = info.Title
  240. for _, capab := range info.Capabilities {
  241. oc.Capabilities[capab] = true
  242. }
  243. if len(info.WhoisLine) > 0 {
  244. oc.WhoisLine = info.WhoisLine
  245. } else {
  246. oc.WhoisLine = "is a"
  247. if strings.Contains(strings.ToLower(string(oc.Title[0])), "aeiou") {
  248. oc.WhoisLine += "n"
  249. }
  250. oc.WhoisLine += " "
  251. oc.WhoisLine += oc.Title
  252. }
  253. ocs[name] = oc
  254. }
  255. if !anyMissing {
  256. // we've got every operclass!
  257. break
  258. }
  259. }
  260. return &ocs, nil
  261. }
  262. // Oper represents a single assembled operator's config.
  263. type Oper struct {
  264. Class *OperClass
  265. WhoisLine string
  266. Vhost string
  267. Pass []byte
  268. Modes string
  269. }
  270. // Operators returns a map of operator configs from the given OperClass and config.
  271. func (conf *Config) Operators(oc *map[string]OperClass) (map[string]Oper, error) {
  272. operators := make(map[string]Oper)
  273. for name, opConf := range conf.Opers {
  274. var oper Oper
  275. // oper name
  276. name, err := CasefoldName(name)
  277. if err != nil {
  278. return nil, fmt.Errorf("Could not casefold oper name: %s", err.Error())
  279. }
  280. oper.Pass = opConf.PasswordBytes()
  281. oper.Vhost = opConf.Vhost
  282. class, exists := (*oc)[opConf.Class]
  283. if !exists {
  284. return nil, fmt.Errorf("Could not load operator [%s] - they use operclass [%s] which does not exist", name, opConf.Class)
  285. }
  286. oper.Class = &class
  287. if len(opConf.WhoisLine) > 0 {
  288. oper.WhoisLine = opConf.WhoisLine
  289. } else {
  290. oper.WhoisLine = class.WhoisLine
  291. }
  292. oper.Modes = strings.TrimSpace(opConf.Modes)
  293. // successful, attach to list of opers
  294. operators[name] = oper
  295. }
  296. return operators, nil
  297. }
  298. // TLSListeners returns a list of TLS listeners and their configs.
  299. func (conf *Config) TLSListeners() map[string]*tls.Config {
  300. tlsListeners := make(map[string]*tls.Config)
  301. for s, tlsListenersConf := range conf.Server.TLSListeners {
  302. config, err := tlsListenersConf.Config()
  303. config.ClientAuth = tls.RequestClientCert
  304. if err != nil {
  305. log.Fatal(err)
  306. }
  307. tlsListeners[s] = config
  308. }
  309. return tlsListeners
  310. }
  311. // LoadConfig loads the given YAML configuration file.
  312. func LoadConfig(filename string) (config *Config, err error) {
  313. data, err := ioutil.ReadFile(filename)
  314. if err != nil {
  315. return nil, err
  316. }
  317. err = yaml.Unmarshal(data, &config)
  318. if err != nil {
  319. return nil, err
  320. }
  321. config.Filename = filename
  322. // we need this so PasswordBytes returns the correct info
  323. if config.Server.Password != "" {
  324. config.Server.PassConfig.Password = config.Server.Password
  325. }
  326. if config.Network.Name == "" {
  327. return nil, errors.New("Network name missing")
  328. }
  329. if config.Server.Name == "" {
  330. return nil, errors.New("Server name missing")
  331. }
  332. if !utils.IsHostname(config.Server.Name) {
  333. return nil, errors.New("Server name must match the format of a hostname")
  334. }
  335. if config.Datastore.Path == "" {
  336. return nil, errors.New("Datastore path missing")
  337. }
  338. if len(config.Server.Listen) == 0 {
  339. return nil, errors.New("Server listening addresses missing")
  340. }
  341. if config.Limits.NickLen < 1 || config.Limits.ChannelLen < 2 || config.Limits.AwayLen < 1 || config.Limits.KickLen < 1 || config.Limits.TopicLen < 1 {
  342. return nil, errors.New("Limits aren't setup properly, check them and make them sane")
  343. }
  344. if config.Server.STS.Enabled {
  345. config.Server.STS.Duration, err = custime.ParseDuration(config.Server.STS.DurationString)
  346. if err != nil {
  347. return nil, fmt.Errorf("Could not parse STS duration: %s", err.Error())
  348. }
  349. if config.Server.STS.Port < 0 || config.Server.STS.Port > 65535 {
  350. return nil, fmt.Errorf("STS port is incorrect, should be 0 if disabled: %d", config.Server.STS.Port)
  351. }
  352. }
  353. if config.Server.ConnectionThrottler.Enabled {
  354. config.Server.ConnectionThrottler.Duration, err = time.ParseDuration(config.Server.ConnectionThrottler.DurationString)
  355. if err != nil {
  356. return nil, fmt.Errorf("Could not parse connection-throttle duration: %s", err.Error())
  357. }
  358. config.Server.ConnectionThrottler.BanDuration, err = time.ParseDuration(config.Server.ConnectionThrottler.BanDurationString)
  359. if err != nil {
  360. return nil, fmt.Errorf("Could not parse connection-throttle ban-duration: %s", err.Error())
  361. }
  362. }
  363. // process webirc blocks
  364. var newWebIRC []webircConfig
  365. for _, webirc := range config.Server.WebIRC {
  366. // skip webirc blocks with no hosts (such as the example one)
  367. if len(webirc.Hosts) == 0 {
  368. continue
  369. }
  370. err = webirc.Populate()
  371. if err != nil {
  372. return nil, fmt.Errorf("Could not parse WebIRC config: %s", err.Error())
  373. }
  374. newWebIRC = append(newWebIRC, webirc)
  375. }
  376. config.Server.WebIRC = newWebIRC
  377. // process limits
  378. if config.Limits.LineLen.Tags < 512 || config.Limits.LineLen.Rest < 512 {
  379. return nil, errors.New("Line lengths must be 512 or greater (check the linelen section under server->limits)")
  380. }
  381. var newLogConfigs []logger.LoggingConfig
  382. for _, logConfig := range config.Logging {
  383. // methods
  384. methods := make(map[string]bool)
  385. for _, method := range strings.Split(logConfig.Method, " ") {
  386. if len(method) > 0 {
  387. methods[strings.ToLower(method)] = true
  388. }
  389. }
  390. if methods["file"] && logConfig.Filename == "" {
  391. return nil, errors.New("Logging configuration specifies 'file' method but 'filename' is empty")
  392. }
  393. logConfig.MethodFile = methods["file"]
  394. logConfig.MethodStdout = methods["stdout"]
  395. logConfig.MethodStderr = methods["stderr"]
  396. // levels
  397. level, exists := logger.LogLevelNames[strings.ToLower(logConfig.LevelString)]
  398. if !exists {
  399. return nil, fmt.Errorf("Could not translate log leve [%s]", logConfig.LevelString)
  400. }
  401. logConfig.Level = level
  402. // types
  403. for _, typeStr := range strings.Split(logConfig.TypeString, " ") {
  404. if len(typeStr) == 0 {
  405. continue
  406. }
  407. if typeStr == "-" {
  408. return nil, errors.New("Encountered logging type '-' with no type to exclude")
  409. }
  410. if typeStr[0] == '-' {
  411. typeStr = typeStr[1:]
  412. logConfig.ExcludedTypes = append(logConfig.ExcludedTypes, typeStr)
  413. } else {
  414. logConfig.Types = append(logConfig.Types, typeStr)
  415. }
  416. }
  417. if len(logConfig.Types) < 1 {
  418. return nil, errors.New("Logger has no types to log")
  419. }
  420. newLogConfigs = append(newLogConfigs, logConfig)
  421. }
  422. config.Logging = newLogConfigs
  423. config.Server.MaxSendQBytes, err = bytefmt.ToBytes(config.Server.MaxSendQString)
  424. if err != nil {
  425. return nil, fmt.Errorf("Could not parse maximum SendQ size (make sure it only contains whole numbers): %s", err.Error())
  426. }
  427. // get language files
  428. config.Languages.Data = make(map[string]LangData)
  429. if config.Languages.Enabled {
  430. files, err := ioutil.ReadDir(config.Languages.Path)
  431. if err != nil {
  432. return nil, fmt.Errorf("Could not load language files: %s", err.Error())
  433. }
  434. for _, f := range files {
  435. // skip dirs
  436. if f.IsDir() {
  437. continue
  438. }
  439. // only load .lang.yaml files
  440. name := f.Name()
  441. if !strings.HasSuffix(strings.ToLower(name), ".lang.yaml") {
  442. continue
  443. }
  444. // don't load our example file in practice
  445. if strings.ToLower(name) == "example.lang.yaml" {
  446. continue
  447. }
  448. data, err = ioutil.ReadFile(filepath.Join(config.Languages.Path, name))
  449. if err != nil {
  450. return nil, fmt.Errorf("Could not load language file [%s]: %s", name, err.Error())
  451. }
  452. var langInfo LangData
  453. err = yaml.Unmarshal(data, &langInfo)
  454. if err != nil {
  455. return nil, fmt.Errorf("Could not parse language file [%s]: %s", name, err.Error())
  456. }
  457. // confirm that values are correct
  458. if langInfo.Code == "en" {
  459. return nil, fmt.Errorf("Cannot have language file with code 'en' (this is the default language using strings inside the server code). If you're making an English variant, name it with a more specific code")
  460. }
  461. if langInfo.Code == "" || langInfo.Name == "" || langInfo.Maintainers == "" {
  462. return nil, fmt.Errorf("Code, name or maintainers is empty in language file [%s]", name)
  463. }
  464. if len(langInfo.Translations) == 0 {
  465. return nil, fmt.Errorf("Language file [%s] contains no translations", name)
  466. }
  467. // check for duplicate languages
  468. _, exists := config.Languages.Data[strings.ToLower(langInfo.Code)]
  469. if exists {
  470. return nil, fmt.Errorf("Language code [%s] defined twice", langInfo.Code)
  471. }
  472. // and insert into lang info
  473. config.Languages.Data[strings.ToLower(langInfo.Code)] = langInfo
  474. }
  475. // confirm that default language exists
  476. if config.Languages.Default == "" {
  477. config.Languages.Default = "en"
  478. } else {
  479. config.Languages.Default = strings.ToLower(config.Languages.Default)
  480. }
  481. _, exists := config.Languages.Data[config.Languages.Default]
  482. if config.Languages.Default != "en" && !exists {
  483. return nil, fmt.Errorf("Cannot find default language [%s]", config.Languages.Default)
  484. }
  485. }
  486. return config, nil
  487. }