Merge pull request #351 from slingamn/changelog.2

changelog and manual updates, plus some last-minute fixes
This commit is contained in:
Daniel Oaks 2019-02-10 20:45:45 +10:00 committed by GitHub
commit 6ca2cb26d7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 116 additions and 51 deletions

View File

@ -4,8 +4,12 @@ All notable changes to Oragono will be documented in this file.
This project adheres to [Semantic Versioning](http://semver.org/). For the purposes of versioning, we consider the "public API" to refer to the configuration files, CLI interface and database format.
## Unreleased
New release of Oragono! Up to 057d00b.
## [0.13.0-rc]
This release has a wide range of improvements and new features. Highlights include:
* Support for storing and replaying message history, via various protocol extensions: the `draft/resume-0.2` capability, the `CHATHISTORY` command, and a custom `HISTORY` command
* Confusables prevention for Unicode nicknames and account names
* User-customizable nickname protection schemes
* A SASL-only mode in which all clients must authenticate with SASL
### Config Changes
* `allow-custom-enforcement` key added under `accounts`.
@ -15,9 +19,12 @@ New release of Oragono! Up to 057d00b.
* `login-throttling` section added under `accounts`.
* `method` key now under `accounts` now allows the value `"optional"`.
* Logging type `server` has been added, replacing the `startup`, `rehash`, and `shutdown` types.
s* We no longer listen on port `6668` by default (this fixes Docker installs).
* We no longer listen on port `6668` by default (this fixes Docker installs).
* The default logging configuration now logs to stderr only, rather than to both stderr and a file
### Security
* Added a SASL-only mode in which all clients must authenticate with SASL
* Added login throttling as a hardening measure against password guessing
### Added
* `oragono genpasswd` now works when piping input in (fixes Docker installs).
@ -31,15 +38,15 @@ s* We no longer listen on port `6668` by default (this fixes Docker installs).
* Added new subcommands to `NICKSERV`, including:
* `PASSWD` to change account passwords.
* `ENFORCE` to set a specific enforcement mechanism on your nick.
* `SAREGISTER` to allow operators to manually create new user accounts
* Added Unicode confusable detection and prevention when changing nicknames and registering accounts.
### Changed
* `SASL PLAIN` logins now log more correctly.
* Database upgrade failures now provide information about the error that occurred.
* Idents are now restricted in the same way as other servers.
* Idents (sometimes called "usernames") are now restricted to ASCII, similar to other servers.
* In addition to the founder, now auto-ops (halfop and higher) automatically bypass channel join restrictions.
* Log lines now display time down to milliseconds, instead of just seconds.
* Logging-in can now be throttled, and is by default.
* Updated all translation files (thanks to our amazing translators!).
* Updated proposed IRCv3 capability to version [`draft/resume-0.2`](https://github.com/ircv3/ircv3-specifications/pull/306).
* When nick ownership is enabled, users can now select which enforcement mechanism to use with their nickname.
@ -54,19 +61,20 @@ s* We no longer listen on port `6668` by default (this fixes Docker installs).
* Channel names with right-to-left characters are now casefolded correctly.
* Fixed incorrect rejection of nickmasks with Unicode RTL nicknames.
* Fixed nickname sync issue which could cause clients to fail to see each other.
* Fixed some internal socker logic, to prevent race conditions.
* Invalid `ISUPPORT` tokens are now explicitly rejected.
* Made `server-time` timestamp format more consistent and safer.
* Oragono now exits with status (1) if it fails to start.
* Prevent logging in multiple times when using `/NS IDENTIFY`.
* Prevented the db handler from automagically creating the database without initializing it (thanks @enckse!). We also now automatically create the datastore on `run`.
* Updated internal command line parsing (thanks @iNecas!).
* Fixed handling of CIDR width in connection limiting/throttling
### Internal Notes
* `DLINE` and `KLINE` refactored, and expired bans are now removed from the database.
* Logging system optimised.
* Services handlers refactored.
* Translations are now sent to/PR'd from CrowdIn automagically as we develop the software.
* Direct responses to client commands are now sent "synchronously", bypassing the sendq
## [0.12.0] - 2018-10-15

View File

@ -117,17 +117,10 @@ In this section, we'll explain and go through using various features of the Orag
In most IRC servers you can use `NickServ` to register an account. You can do the same thing with Oragono, by default, with no other software needed!
However, there are some differences between how Oragono handles accounts and how most other servers do. Some of these differences are that:
- In Oragono, account names are completely unrelated to nicknames.
- In Oragono, there's no nickname ownership unless you configure a config section.
With nickname ownership and account names, on most IRC servers your nickname and your account name are one and the same thing. This isn't the case with Oragono. When using Oragono, your nickname and account name are totally unrelated. However, you can enable nickname ownership with the `nick-reservation` section in the config.
These are the two ways you can register an account:
/QUOTE ACC REGISTER <username> * passphrase :<password>
/NS REGISTER <username> <password>
/NS REGISTER <username> * <password>
This is the way to go if you want to use a regular password. `<username>` and `<password>` are your username and password, respectively (make sure the leave that one `:` before your actual password!).
@ -138,6 +131,57 @@ If you want to use a TLS client certificate to authenticate (`SASL CERTFP`), the
Once you've registered, you'll need to setup SASL to login (or use NickServ IDENTIFY). One of the more complete SASL instruction pages is Freenode's page [here](https://freenode.net/kb/answer/sasl). Open up that page, find your IRC client and then setup SASL with your chosen username and password!
## Nickname reservation
Oragono supports several different modes of operation with respect to accounts and nicknames.
### Traditional / lenient mode
This is the mode that matches the typical pre-modern ircd behavior. In this mode, there is no connection between account names and nicknames. Anyone can use any nickname (as long as it's not already in use by another running client). However, accounts are still useful: they can be used to register channels (see below), and some IRCv3-capable clients (with the `account-tag` or `extended-join` capabilities) may be able to take advantage of them.
To enable this mode, set the following configs (they are the defaults):
* `accounts.registration.enabled = true`
* `accounts.authentication-enabled = true`
* `accounts.nick-reservation.enabled = false`
### Nick reservation
This is the mode corresponding to a typical IRC network with a service system (like Freenode). In this mode, registering an account gives you privileges over the use of that account as a nickname. The server will then help you to enforce control over your nickname(s):
* You can proactively prevent anyone from using your nickname, unless they're already logged into your account
* Alternately, you can give clients a grace period to log into your account, but if they don't and the grace period expires, the server will change their nickname to something else
* Alternately, you can forego any proactive enforcement --- but if you decide you want to reclaim your nickname from a squatter, you can `/msg Nickserv ghost stolen_nickname` and they'll be disconnected
* You can associate additional nicknames with your account by changing to it and then issuing `/msg nickserv group`
To enable this mode, set the following configs:
* `accounts.registration.enabled = true`
* `accounts.authentication-enabled = true`
* `accounts.nick-reservation.enabled = true`
The following additional configs are recommended:
* `accounts.nick-reservation.method = timeout` ; setting `strict` here effectively forces people to use SASL, and some popular clients either do not support SASL, or have bugs in their SASL implementations.
* `accounts.nick-reservation.allow-custom-enforcement = true` ; this allows people to opt into strict enforcement, or opt out of enforcement. For details on how to do this, `/msg nickserv help enforce`.
### SASL-only mode
This mode is comparable to Slack, Mattermost, or similar products intended as internal chat servers for an organization or team. In this mode, clients cannot connect to the server unless they log in with SASL as part of the initial handshake. This allows Oragono to be deployed facing the public Internet, with fine-grained control over who can log in.
In this mode, clients must have a valid account to connect, so they cannot register their own accounts. Accordingly, an operator must do the initial account creation, using the `SAREGISTER` command of NickServ. (For more details, `/msg nickserv help saregister`.) To bootstrap this process, the SASL requirement can be disabled initially so that a first account can be created. Alternately, connections from localhost are exempt (by default) from the SASL requirement.
To enable this mode, set the following configs:
* `accounts.registration.enabled = true`
* `accounts.authentication-enabled = true`
* `accounts.require-sasl.enabled = true`
* `accounts.nick-reservation.enabled = true`
Additionally, the following config is recommended:
* `accounts.nick-reservation.method = strict`
## Channel Registration
@ -149,6 +193,8 @@ To register a channel, make sure you're joined to it and logged into your accoun
For example, `/CS REGISTER #channel` will register the channel `#test` to my account. If you have a registered channel, you can use `/CS OP #channel` to regain ops in it. Right now, the options for a registered channel are pretty sparse, but we'll add more as we go along.
If your friends have registered accounts, you can automatically grant them operator permissions when they join the channel. For more details, see `/CS HELP AMODE`.
## Language

View File

@ -306,8 +306,16 @@ func (am *AccountManager) Register(client *Client, account string, callbackNames
return errAccountAlreadyRegistered
}
// can't register a guest nickname
config := am.server.AccountConfig()
// if nick reservation is enabled, you can only register your current nickname
// as an account; this prevents "land-grab" situations where someone else
// registers your nick out from under you and then NS GHOSTs you
// n.b. client is nil during a SAREGISTER:
if config.NickReservation.Enabled && client != nil && client.Nick() != account {
return errAccountMustHoldNick
}
// can't register a guest nickname
renamePrefix := strings.ToLower(config.NickReservation.RenamePrefix)
if renamePrefix != "" && strings.HasPrefix(casefoldedAccount, renamePrefix) {
return errAccountAlreadyRegistered

View File

@ -9,14 +9,14 @@ import "errors"
// Runtime Errors
var (
errAccountAlreadyRegistered = errors.New("Account already exists")
errAccountAlreadyVerified = errors.New("Account is already verified")
errAccountAlreadyRegistered = errors.New(`Account already exists`)
errAccountAlreadyVerified = errors.New(`Account is already verified`)
errAccountCantDropPrimaryNick = errors.New("Can't unreserve primary nickname")
errAccountCreation = errors.New("Account could not be created")
errAccountCredUpdate = errors.New("Could not update password hash to new method")
errAccountDoesNotExist = errors.New("Account does not exist")
errAccountInvalidCredentials = errors.New("Invalid account credentials")
errAccountBadPassphrase = errors.New("Passphrase contains forbidden characters or is otherwise invalid")
errAccountBadPassphrase = errors.New(`Passphrase contains forbidden characters or is otherwise invalid`)
errAccountNickReservationFailed = errors.New("Could not (un)reserve nick")
errAccountNotLoggedIn = errors.New("You're not logged into an account")
errAccountTooManyNicks = errors.New("Account has too many reserved nicks")
@ -24,8 +24,9 @@ var (
errAccountVerificationFailed = errors.New("Account verification failed")
errAccountVerificationInvalidCode = errors.New("Invalid account verification code")
errAccountUpdateFailed = errors.New("Error while updating your account information")
errAccountMustHoldNick = errors.New(`You must hold that nickname in order to register it`)
errCallbackFailed = errors.New("Account verification could not be sent")
errCertfpAlreadyExists = errors.New("An account already exists with your certificate")
errCertfpAlreadyExists = errors.New(`An account already exists for your certificate fingerprint`)
errChannelAlreadyRegistered = errors.New("Channel is already registered")
errChannelNameInUse = errors.New("Channel name in use")
errInvalidChannelName = errors.New("Invalid channel name")

View File

@ -153,19 +153,7 @@ func accRegisterHandler(server *Server, client *Client, msg ircmsg.IrcMessage, r
err = server.accounts.Register(client, account, callbackNamespace, callbackValue, passphrase, certfp)
if err != nil {
msg := "Unknown"
code := ERR_UNKNOWNERROR
if err == errCertfpAlreadyExists {
msg = "An account already exists for your certificate fingerprint"
} else if err == errAccountAlreadyRegistered {
msg = "Account already exists"
code = ERR_ACCOUNT_ALREADY_EXISTS
} else if err == errAccountBadPassphrase {
msg = "Passphrase contains forbidden characters or is otherwise invalid"
}
if err == errAccountAlreadyRegistered || err == errAccountCreation || err == errCertfpAlreadyExists {
msg = err.Error()
}
msg, code := registrationErrorToMessageAndCode(err)
rb.Add(nil, server.name, code, nick, "ACC", "REGISTER", client.t(msg))
return false
}
@ -186,6 +174,21 @@ func accRegisterHandler(server *Server, client *Client, msg ircmsg.IrcMessage, r
return false
}
func registrationErrorToMessageAndCode(err error) (message, numeric string) {
// default responses: let's be risk-averse about displaying internal errors
// to the clients, especially for something as sensitive as accounts
message = `Could not register`
numeric = ERR_UNKNOWNERROR
switch err {
case errAccountAlreadyRegistered, errAccountAlreadyVerified:
message = err.Error()
numeric = ERR_ACCOUNT_ALREADY_EXISTS
case errAccountCreation, errAccountMustHoldNick, errAccountBadPassphrase, errCertfpAlreadyExists:
message = err.Error()
}
return
}
// helper function to dispatch messages when a client successfully registers
func sendSuccessfulRegResponse(client *Client, rb *ResponseBuffer, forNS bool) {
if forNS {

View File

@ -339,6 +339,10 @@ func nsRegisterHandler(server *Server, client *Client, command string, params []
return
}
if !nsLoginThrottleCheck(client, rb) {
return
}
config := server.AccountConfig()
var callbackNamespace, callbackValue string
noneCallbackAllowed := false
@ -376,14 +380,7 @@ func nsRegisterHandler(server *Server, client *Client, command string, params []
// details could not be stored and relevant numerics have been dispatched, abort
if err != nil {
errMsg := client.t("Could not register")
if err == errCertfpAlreadyExists {
errMsg = client.t("An account already exists for your certificate fingerprint")
} else if err == errAccountAlreadyRegistered || err == errAccountAlreadyVerified {
errMsg = client.t("Account already exists")
} else if err == errAccountBadPassphrase {
errMsg = client.t("Passphrase contains forbidden characters or is otherwise invalid")
}
errMsg, _ := registrationErrorToMessageAndCode(err)
nsNotice(rb, errMsg)
return
}

View File

@ -261,7 +261,7 @@ func (server *Server) acceptClient(conn clientConn) {
}
}
server.logger.Debug("localconnect-ip", fmt.Sprintf("Client connecting from %v", ipaddr))
server.logger.Info("localconnect-ip", fmt.Sprintf("Client connecting from %v", ipaddr))
// prolly don't need to alert snomasks on this, only on connection reg
NewClient(server, conn.Conn, conn.IsTLS)
@ -421,7 +421,7 @@ func (server *Server) tryRegister(c *Client) {
server.stats.ChangeTotal(1)
// continue registration
server.logger.Debug("localconnect", fmt.Sprintf("Client connected [%s] [u:%s] [r:%s]", c.nick, c.username, c.realname))
server.logger.Info("localconnect", fmt.Sprintf("Client connected [%s] [u:%s] [r:%s]", c.nick, c.username, c.realname))
server.snomasks.Send(sno.LocalConnects, fmt.Sprintf("Client connected [%s] [u:%s] [h:%s] [ip:%s] [r:%s]", c.nick, c.username, c.rawHostname, c.IPString(), c.realname))
// "register"; this includes the initial phase of session resumption

View File

@ -98,7 +98,7 @@ server:
allow-plaintext-resume: false
# maximum length of clients' sendQ in bytes
# this should be big enough to hold /LIST and HELP replies
# this should be big enough to hold bursts of channel/direct messages
max-sendq: 16k
# maximum number of connections per subnet
@ -354,10 +354,11 @@ logging:
# file log to given target filename
# stdout log to stdout
# stderr log to stderr
method: file stderr
# (you can specify multiple methods, e.g., to log to both stderr and a file)
method: stderr
# filename to log to, if file method is selected
filename: ircd.log
# filename: ircd.log
# type(s) of logs to keep here. you can use - to exclude those types
#
@ -374,15 +375,16 @@ logging:
# password password hashing and comparing
# userinput raw lines sent by users
# useroutput raw lines sent to users
type: "* -userinput -useroutput -localconnect -localconnect-ip"
type: "* -userinput -useroutput"
# one of: debug info warn error
level: info
-
# avoid logging IP addresses to file
method: stderr
type: localconnect localconnect-ip
level: debug
#-
# # example of a file log that avoids logging IP addresses
# method: file
# filename: ircd.log
# type: "* -userinput -useroutput -localconnect -localconnect-ip"
# level: debug
# debug options
debug: