You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

config.go 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435
  1. // Copyright (c) 2012-2014 Jeremy Latt
  2. // Copyright (c) 2014-2015 Edmund Huber
  3. // Copyright (c) 2016- 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. "strings"
  13. "time"
  14. "github.com/DanielOaks/oragono/irc/custime"
  15. "gopkg.in/yaml.v2"
  16. )
  17. type PassConfig struct {
  18. Password string
  19. }
  20. // TLSListenConfig defines configuration options for listening on TLS
  21. type TLSListenConfig struct {
  22. Cert string
  23. Key string
  24. }
  25. // Certificate returns the TLS certificate assicated with this TLSListenConfig
  26. func (conf *TLSListenConfig) Config() (*tls.Config, error) {
  27. cert, err := tls.LoadX509KeyPair(conf.Cert, conf.Key)
  28. if err != nil {
  29. return nil, errors.New("tls cert+key: invalid pair")
  30. }
  31. return &tls.Config{
  32. Certificates: []tls.Certificate{cert},
  33. }, err
  34. }
  35. func (conf *PassConfig) PasswordBytes() []byte {
  36. bytes, err := DecodePasswordHash(conf.Password)
  37. if err != nil {
  38. log.Fatal("decode password error: ", err)
  39. }
  40. return bytes
  41. }
  42. type AccountRegistrationConfig struct {
  43. Enabled bool
  44. EnabledCallbacks []string `yaml:"enabled-callbacks"`
  45. Callbacks struct {
  46. Mailto struct {
  47. Server string
  48. Port int
  49. TLS struct {
  50. Enabled bool
  51. InsecureSkipVerify bool `yaml:"insecure_skip_verify"`
  52. ServerName string `yaml:"servername"`
  53. }
  54. Username string
  55. Password string
  56. Sender string
  57. VerifyMessageSubject string `yaml:"verify-message-subject"`
  58. VerifyMessage string `yaml:"verify-message"`
  59. }
  60. }
  61. }
  62. type OperClassConfig struct {
  63. Title string
  64. WhoisLine string
  65. Extends string
  66. Capabilities []string
  67. }
  68. type OperConfig struct {
  69. Class string
  70. Vhost string
  71. WhoisLine string `yaml:"whois-line"`
  72. Password string
  73. }
  74. func (conf *OperConfig) PasswordBytes() []byte {
  75. bytes, err := DecodePasswordHash(conf.Password)
  76. if err != nil {
  77. log.Fatal("decode password error: ", err)
  78. }
  79. return bytes
  80. }
  81. type RestAPIConfig struct {
  82. Enabled bool
  83. Listen string
  84. }
  85. type ConnectionLimitsConfig struct {
  86. Enabled bool
  87. CidrLenIPv4 int `yaml:"cidr-len-ipv4"`
  88. CidrLenIPv6 int `yaml:"cidr-len-ipv6"`
  89. IPsPerCidr int `yaml:"ips-per-subnet"`
  90. Exempted []string
  91. }
  92. type ConnectionThrottleConfig struct {
  93. Enabled bool
  94. CidrLenIPv4 int `yaml:"cidr-len-ipv4"`
  95. CidrLenIPv6 int `yaml:"cidr-len-ipv6"`
  96. ConnectionsPerCidr int `yaml:"max-connections"`
  97. DurationString string `yaml:"duration"`
  98. Duration time.Duration `yaml:"duration-time"`
  99. BanDurationString string `yaml:"ban-duration"`
  100. BanDuration time.Duration
  101. BanMessage string `yaml:"ban-message"`
  102. Exempted []string
  103. }
  104. type LoggingConfig struct {
  105. Method string
  106. Methods map[string]bool
  107. Filename string
  108. TypeString string `yaml:"type"`
  109. Types map[string]bool `yaml:"real-types"`
  110. ExcludedTypes map[string]bool `yaml:"real-excluded-types"`
  111. LevelString string `yaml:"level"`
  112. Level LogLevel `yaml:"level-real"`
  113. }
  114. type LineLenConfig struct {
  115. Tags int
  116. Rest int
  117. }
  118. type STSConfig struct {
  119. Enabled bool
  120. Duration time.Duration `yaml:"duration-real"`
  121. DurationString string `yaml:"duration"`
  122. Port int
  123. Preload bool
  124. }
  125. // Value returns the STS value to advertise in CAP
  126. func (sts *STSConfig) Value() string {
  127. val := fmt.Sprintf("duration=%d,", int(sts.Duration.Seconds()))
  128. if sts.Enabled && sts.Port > 0 {
  129. val += fmt.Sprintf(",port=%d", sts.Port)
  130. }
  131. if sts.Enabled && sts.Preload {
  132. val += ",preload"
  133. }
  134. return val
  135. }
  136. type Config struct {
  137. Network struct {
  138. Name string
  139. }
  140. Server struct {
  141. PassConfig
  142. Password string
  143. Name string
  144. Listen []string
  145. Wslisten string `yaml:"ws-listen"`
  146. TLSListeners map[string]*TLSListenConfig `yaml:"tls-listeners"`
  147. STS STSConfig
  148. RestAPI RestAPIConfig `yaml:"rest-api"`
  149. CheckIdent bool `yaml:"check-ident"`
  150. MOTD string
  151. ConnectionLimits ConnectionLimitsConfig `yaml:"connection-limits"`
  152. ConnectionThrottle ConnectionThrottleConfig `yaml:"connection-throttling"`
  153. }
  154. Datastore struct {
  155. Path string
  156. }
  157. Accounts struct {
  158. Registration AccountRegistrationConfig
  159. AuthenticationEnabled bool `yaml:"authentication-enabled"`
  160. }
  161. OperClasses map[string]*OperClassConfig `yaml:"oper-classes"`
  162. Opers map[string]*OperConfig
  163. Logging []LoggingConfig
  164. Limits struct {
  165. AwayLen uint `yaml:"awaylen"`
  166. ChanListModes uint `yaml:"chan-list-modes"`
  167. ChannelLen uint `yaml:"channellen"`
  168. KickLen uint `yaml:"kicklen"`
  169. MonitorEntries uint `yaml:"monitor-entries"`
  170. NickLen uint `yaml:"nicklen"`
  171. TopicLen uint `yaml:"topiclen"`
  172. WhowasEntries uint `yaml:"whowas-entries"`
  173. LineLen LineLenConfig `yaml:"linelen"`
  174. }
  175. }
  176. type OperClass struct {
  177. Title string
  178. WhoisLine string `yaml:"whois-line"`
  179. Capabilities map[string]bool // map to make lookups much easier
  180. }
  181. func (conf *Config) OperatorClasses() (*map[string]OperClass, error) {
  182. ocs := make(map[string]OperClass)
  183. // loop from no extends to most extended, breaking if we can't add any more
  184. lenOfLastOcs := -1
  185. for {
  186. if lenOfLastOcs == len(ocs) {
  187. return nil, errors.New("OperClasses contains a looping dependency, or a class extends from a class that doesn't exist")
  188. }
  189. lenOfLastOcs = len(ocs)
  190. var anyMissing bool
  191. for name, info := range conf.OperClasses {
  192. _, exists := ocs[name]
  193. _, extendsExists := ocs[info.Extends]
  194. if exists {
  195. // class already exists
  196. continue
  197. } else if len(info.Extends) > 0 && !extendsExists {
  198. // class we extend on doesn't exist
  199. _, exists := conf.OperClasses[info.Extends]
  200. if !exists {
  201. return nil, fmt.Errorf("Operclass [%s] extends [%s], which doesn't exist", name, info.Extends)
  202. }
  203. anyMissing = true
  204. continue
  205. }
  206. // create new operclass
  207. var oc OperClass
  208. oc.Capabilities = make(map[string]bool)
  209. // get inhereted info from other operclasses
  210. if len(info.Extends) > 0 {
  211. einfo, _ := ocs[info.Extends]
  212. for capab := range einfo.Capabilities {
  213. oc.Capabilities[capab] = true
  214. }
  215. }
  216. // add our own info
  217. oc.Title = info.Title
  218. for _, capab := range info.Capabilities {
  219. oc.Capabilities[capab] = true
  220. }
  221. if len(info.WhoisLine) > 0 {
  222. oc.WhoisLine = info.WhoisLine
  223. } else {
  224. oc.WhoisLine = "is a"
  225. if strings.Contains(strings.ToLower(string(oc.Title[0])), "aeiou") {
  226. oc.WhoisLine += "n"
  227. }
  228. oc.WhoisLine += " "
  229. oc.WhoisLine += oc.Title
  230. }
  231. ocs[name] = oc
  232. }
  233. if !anyMissing {
  234. // we've got every operclass!
  235. break
  236. }
  237. }
  238. return &ocs, nil
  239. }
  240. type Oper struct {
  241. Class *OperClass
  242. WhoisLine string
  243. Vhost string
  244. Pass []byte
  245. }
  246. func (conf *Config) Operators(oc *map[string]OperClass) (map[string]Oper, error) {
  247. operators := make(map[string]Oper)
  248. for name, opConf := range conf.Opers {
  249. var oper Oper
  250. // oper name
  251. name, err := CasefoldName(name)
  252. if err != nil {
  253. return nil, fmt.Errorf("Could not casefold oper name: %s", err.Error())
  254. }
  255. oper.Pass = opConf.PasswordBytes()
  256. oper.Vhost = opConf.Vhost
  257. class, exists := (*oc)[opConf.Class]
  258. if !exists {
  259. return nil, fmt.Errorf("Could not load operator [%s] - they use operclass [%s] which does not exist", name, opConf.Class)
  260. }
  261. oper.Class = &class
  262. if len(opConf.WhoisLine) > 0 {
  263. oper.WhoisLine = opConf.WhoisLine
  264. } else {
  265. oper.WhoisLine = class.WhoisLine
  266. }
  267. // successful, attach to list of opers
  268. operators[name] = oper
  269. }
  270. return operators, nil
  271. }
  272. func (conf *Config) TLSListeners() map[string]*tls.Config {
  273. tlsListeners := make(map[string]*tls.Config)
  274. for s, tlsListenersConf := range conf.Server.TLSListeners {
  275. config, err := tlsListenersConf.Config()
  276. if err != nil {
  277. log.Fatal(err)
  278. }
  279. name, err := CasefoldName(s)
  280. if err == nil {
  281. tlsListeners[name] = config
  282. } else {
  283. log.Println("Could not casefold TLS listener:", err.Error())
  284. }
  285. }
  286. return tlsListeners
  287. }
  288. func LoadConfig(filename string) (config *Config, err error) {
  289. data, err := ioutil.ReadFile(filename)
  290. if err != nil {
  291. return nil, err
  292. }
  293. err = yaml.Unmarshal(data, &config)
  294. if err != nil {
  295. return nil, err
  296. }
  297. // we need this so PasswordBytes returns the correct info
  298. if config.Server.Password != "" {
  299. config.Server.PassConfig.Password = config.Server.Password
  300. }
  301. if config.Network.Name == "" {
  302. return nil, errors.New("Network name missing")
  303. }
  304. if config.Server.Name == "" {
  305. return nil, errors.New("Server name missing")
  306. }
  307. if !IsHostname(config.Server.Name) {
  308. return nil, errors.New("Server name must match the format of a hostname")
  309. }
  310. if config.Datastore.Path == "" {
  311. return nil, errors.New("Datastore path missing")
  312. }
  313. if len(config.Server.Listen) == 0 {
  314. return nil, errors.New("Server listening addresses missing")
  315. }
  316. if config.Limits.NickLen < 1 || config.Limits.ChannelLen < 2 || config.Limits.AwayLen < 1 || config.Limits.KickLen < 1 || config.Limits.TopicLen < 1 {
  317. return nil, errors.New("Limits aren't setup properly, check them and make them sane")
  318. }
  319. if config.Server.STS.Enabled {
  320. config.Server.STS.Duration, err = custime.ParseDuration(config.Server.STS.DurationString)
  321. if err != nil {
  322. return nil, fmt.Errorf("Could not parse STS duration: %s", err.Error())
  323. }
  324. if config.Server.STS.Port < 0 || config.Server.STS.Port > 65535 {
  325. return nil, fmt.Errorf("STS port is incorrect, should be 0 if disabled: %d", config.Server.STS.Port)
  326. }
  327. }
  328. if config.Server.ConnectionThrottle.Enabled {
  329. config.Server.ConnectionThrottle.Duration, err = time.ParseDuration(config.Server.ConnectionThrottle.DurationString)
  330. if err != nil {
  331. return nil, fmt.Errorf("Could not parse connection-throttle duration: %s", err.Error())
  332. }
  333. config.Server.ConnectionThrottle.BanDuration, err = time.ParseDuration(config.Server.ConnectionThrottle.BanDurationString)
  334. if err != nil {
  335. return nil, fmt.Errorf("Could not parse connection-throttle ban-duration: %s", err.Error())
  336. }
  337. }
  338. if config.Limits.LineLen.Tags < 512 || config.Limits.LineLen.Rest < 512 {
  339. return nil, errors.New("Line lengths must be 512 or greater (check the linelen section under server->limits)")
  340. }
  341. var newLogConfigs []LoggingConfig
  342. for _, logConfig := range config.Logging {
  343. // methods
  344. logConfig.Methods = make(map[string]bool)
  345. for _, method := range strings.Split(logConfig.Method, " ") {
  346. if len(method) > 0 {
  347. logConfig.Methods[strings.ToLower(method)] = true
  348. }
  349. }
  350. if logConfig.Methods["file"] && logConfig.Filename == "" {
  351. return nil, errors.New("Logging configuration specifies 'file' method but 'filename' is empty")
  352. }
  353. // levels
  354. level, exists := logLevelNames[strings.ToLower(logConfig.LevelString)]
  355. if !exists {
  356. return nil, fmt.Errorf("Could not translate log leve [%s]", logConfig.LevelString)
  357. }
  358. logConfig.Level = level
  359. // types
  360. logConfig.Types = make(map[string]bool)
  361. logConfig.ExcludedTypes = make(map[string]bool)
  362. for _, typeStr := range strings.Split(logConfig.TypeString, " ") {
  363. if len(typeStr) == 0 {
  364. continue
  365. }
  366. if typeStr == "-" {
  367. return nil, errors.New("Encountered logging type '-' with no type to exclude")
  368. }
  369. if typeStr[0] == '-' {
  370. typeStr = typeStr[1:]
  371. logConfig.ExcludedTypes[typeStr] = true
  372. } else {
  373. logConfig.Types[typeStr] = true
  374. }
  375. }
  376. if len(logConfig.Types) < 1 {
  377. return nil, errors.New("Logger has no types to log")
  378. }
  379. newLogConfigs = append(newLogConfigs, logConfig)
  380. }
  381. config.Logging = newLogConfigs
  382. return config, nil
  383. }