From 52d6fa70a8e356013e16ef3dc52fc436d87b6084 Mon Sep 17 00:00:00 2001 From: Shivaram Lingamneni Date: Wed, 22 Apr 2020 21:44:04 -0400 Subject: [PATCH 01/24] fix #954 --- distrib/systemd/oragono.service | 19 +++++++++++++++++++ docs/MANUAL.md | 33 ++++++++++++++++++++++++++++++--- 2 files changed, 49 insertions(+), 3 deletions(-) create mode 100644 distrib/systemd/oragono.service diff --git a/distrib/systemd/oragono.service b/distrib/systemd/oragono.service new file mode 100644 index 00000000..6af6ad6f --- /dev/null +++ b/distrib/systemd/oragono.service @@ -0,0 +1,19 @@ +[Unit] +Description=oragono +After=network.target +# If you are using MySQL for history storage, comment out the above line +# and uncomment these two instead: +# Requires=mysql.service +# After=network.target mysql.service + +[Service] +Type=simple +User=oragono +WorkingDirectory=/home/oragono +ExecStart=/home/oragono/oragono run --conf /home/oragono/ircd.yaml +ExecReload=/bin/kill -HUP $MAINPID +Restart=on-failure +LimitNOFILE=1048576 + +[Install] +WantedBy=multi-user.target diff --git a/docs/MANUAL.md b/docs/MANUAL.md index 8f8fd901..a122cbe7 100644 --- a/docs/MANUAL.md +++ b/docs/MANUAL.md @@ -22,6 +22,7 @@ _Copyright © Daniel Oaks , Shivaram Lingamneni Date: Wed, 22 Apr 2020 21:52:24 -0400 Subject: [PATCH 02/24] fix #950 --- irc/channel.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/irc/channel.go b/irc/channel.go index f0ff6cef..0fb3f8b6 100644 --- a/irc/channel.go +++ b/irc/channel.go @@ -541,10 +541,16 @@ func (channel *Channel) ClientPrefixes(client *Client, isMultiPrefix bool) strin func (channel *Channel) ClientHasPrivsOver(client *Client, target *Client) bool { channel.stateMutex.RLock() + founder := channel.registeredFounder clientModes := channel.members[client] targetModes := channel.members[target] channel.stateMutex.RUnlock() + if founder != "" && founder == client.Account() { + // #950: founder can kick or whatever without actually having the +q mode + return true + } + return channelUserModeHasPrivsOver(clientModes.HighestChannelUserMode(), targetModes.HighestChannelUserMode()) } From bd088ea197d99ca02dba44952e5c2b07c96c3237 Mon Sep 17 00:00:00 2001 From: Shivaram Lingamneni Date: Wed, 22 Apr 2020 22:11:44 -0400 Subject: [PATCH 03/24] fix #951 --- irc/handlers.go | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/irc/handlers.go b/irc/handlers.go index 31d4eae6..3f7416ee 100644 --- a/irc/handlers.go +++ b/irc/handlers.go @@ -2054,7 +2054,7 @@ func operHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Resp } // must pass at least one check, and all enabled checks - var checkPassed, checkFailed bool + var checkPassed, checkFailed, passwordFailed bool oper := server.GetOperator(msg.Params[0]) if oper != nil { if oper.Fingerprint != "" { @@ -2065,8 +2065,11 @@ func operHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Resp } } if !checkFailed && oper.Pass != nil { - if len(msg.Params) == 1 || bcrypt.CompareHashAndPassword(oper.Pass, []byte(msg.Params[1])) != nil { + if len(msg.Params) == 1 { checkFailed = true + } else if bcrypt.CompareHashAndPassword(oper.Pass, []byte(msg.Params[1])) != nil { + checkFailed = true + passwordFailed = true } else { checkPassed = true } @@ -2075,11 +2078,18 @@ func operHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Resp if !checkPassed || checkFailed { rb.Add(nil, server.name, ERR_PASSWDMISMATCH, client.Nick(), client.t("Password incorrect")) - client.Quit(client.t("Password incorrect"), rb.session) - return true + // #951: only disconnect them if we actually tried to check a password for them + if passwordFailed { + client.Quit(client.t("Password incorrect"), rb.session) + return true + } else { + return false + } } - applyOper(client, oper, rb) + if oper != nil { + applyOper(client, oper, rb) + } return false } From eebe681538723e9a03f2de7c1c86a3007f7dc9f6 Mon Sep 17 00:00:00 2001 From: Shivaram Lingamneni Date: Wed, 22 Apr 2020 22:51:19 -0400 Subject: [PATCH 04/24] fix #532 --- irc/channel.go | 37 ++++++++++++++++++++++++++++++++----- irc/chanserv.go | 6 +++--- irc/handlers.go | 23 +++++++++++++++++------ irc/history/history.go | 1 + 4 files changed, 53 insertions(+), 14 deletions(-) diff --git a/irc/channel.go b/irc/channel.go index 0fb3f8b6..c9adaf09 100644 --- a/irc/channel.go +++ b/irc/channel.go @@ -1070,6 +1070,25 @@ func (channel *Channel) replayHistoryItems(rb *ResponseBuffer, items []history.I message := fmt.Sprintf(client.t("%[1]s changed nick to %[2]s"), nick, item.Params[0]) rb.AddFromClient(item.Message.Time, utils.MungeSecretToken(item.Message.Msgid), histServMask, "*", nil, "PRIVMSG", chname, message) } + case history.Topic: + if eventPlayback { + rb.AddFromClient(item.Message.Time, item.Message.Msgid, item.Nick, item.AccountName, nil, "TOPIC", chname, item.Message.Message) + } else { + message := fmt.Sprintf(client.t("%[1]s set the channel topic to: %[2]s"), nick, item.Message.Message) + rb.AddFromClient(item.Message.Time, utils.MungeSecretToken(item.Message.Msgid), histServMask, "*", nil, "PRIVMSG", chname, message) + } + case history.Mode: + params := make([]string, len(item.Message.Split)+1) + params[0] = chname + for i, pair := range item.Message.Split { + params[i+1] = pair.Message + } + if eventPlayback { + rb.AddFromClient(item.Message.Time, item.Message.Msgid, item.Nick, item.AccountName, nil, "MODE", params...) + } else { + message := fmt.Sprintf(client.t("%[1]s set channel modes: %[2]s"), nick, strings.Join(params[1:], " ")) + rb.AddFromClient(item.Message.Time, utils.MungeSecretToken(item.Message.Msgid), histServMask, "*", nil, "PRIVMSG", chname, message) + } } } } @@ -1119,22 +1138,30 @@ func (channel *Channel) SetTopic(client *Client, topic string, rb *ResponseBuffe } channel.stateMutex.Lock() + chname := channel.name channel.topic = topic channel.topicSetBy = client.nickMaskString channel.topicSetTime = time.Now().UTC() channel.stateMutex.Unlock() - prefix := client.NickMaskString() + details := client.Details() + message := utils.MakeMessage(topic) + rb.AddFromClient(message.Time, message.Msgid, details.nickMask, details.accountName, nil, "TOPIC", chname, topic) for _, member := range channel.Members() { for _, session := range member.Sessions() { - if session == rb.session { - rb.Add(nil, prefix, "TOPIC", channel.name, topic) - } else { - session.Send(nil, prefix, "TOPIC", channel.name, topic) + if session != rb.session { + session.sendFromClientInternal(false, message.Time, message.Msgid, details.nickMask, details.accountName, nil, "TOPIC", chname, topic) } } } + channel.AddHistoryItem(history.Item{ + Type: history.Topic, + Nick: details.nickMask, + AccountName: details.accountName, + Message: message, + }) + channel.MarkDirty(IncludeTopic) } diff --git a/irc/chanserv.go b/irc/chanserv.go index 1aa53132..2a08d313 100644 --- a/irc/chanserv.go +++ b/irc/chanserv.go @@ -244,7 +244,7 @@ func csAmodeHandler(server *Server, client *Client, command string, params []str if member.Account() == change.Arg { applied, change := channel.applyModeToMember(client, change, rb) if applied { - announceCmodeChanges(channel, modes.ModeChanges{change}, chanservMask, rb) + announceCmodeChanges(channel, modes.ModeChanges{change}, chanservMask, "*", rb) } } } @@ -291,7 +291,7 @@ func csOpHandler(server *Server, client *Client, command string, params []string }, rb) if applied { - announceCmodeChanges(channelInfo, modes.ModeChanges{change}, chanservMask, rb) + announceCmodeChanges(channelInfo, modes.ModeChanges{change}, chanservMask, "*", rb) } csNotice(rb, fmt.Sprintf(client.t("Successfully op'd in channel %s"), channelName)) @@ -343,7 +343,7 @@ func csRegisterHandler(server *Server, client *Client, command string, params [] }, rb) if applied { - announceCmodeChanges(channelInfo, modes.ModeChanges{change}, chanservMask, rb) + announceCmodeChanges(channelInfo, modes.ModeChanges{change}, chanservMask, "*", rb) } } diff --git a/irc/handlers.go b/irc/handlers.go index 3f7416ee..86af4a1e 100644 --- a/irc/handlers.go +++ b/irc/handlers.go @@ -1520,24 +1520,35 @@ func cmodeHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Res } // process mode changes, include list operations (an empty set of changes does a list) applied := channel.ApplyChannelModeChanges(client, msg.Command == "SAMODE", changes, rb) - announceCmodeChanges(channel, applied, client.NickMaskString(), rb) + details := client.Details() + announceCmodeChanges(channel, applied, details.nickMask, details.accountName, rb) return false } -func announceCmodeChanges(channel *Channel, applied modes.ModeChanges, source string, rb *ResponseBuffer) { +func announceCmodeChanges(channel *Channel, applied modes.ModeChanges, source, accountName string, rb *ResponseBuffer) { // send out changes if len(applied) > 0 { - //TODO(dan): we should change the name of String and make it return a slice here - args := append([]string{channel.name}, applied.Strings()...) - rb.Add(nil, source, "MODE", args...) + message := utils.MakeMessage("") + changeStrings := applied.Strings() + for _, changeString := range changeStrings { + message.Split = append(message.Split, utils.MessagePair{Message: changeString}) + } + args := append([]string{channel.name}, changeStrings...) + rb.AddFromClient(message.Time, message.Msgid, source, accountName, nil, "MODE", args...) for _, member := range channel.Members() { for _, session := range member.Sessions() { if session != rb.session { - session.Send(nil, source, "MODE", args...) + session.sendFromClientInternal(false, message.Time, message.Msgid, source, accountName, nil, "MODE", args...) } } } + channel.AddHistoryItem(history.Item{ + Type: history.Mode, + Nick: source, + AccountName: accountName, + Message: message, + }) } } diff --git a/irc/history/history.go b/irc/history/history.go index b7317943..5a1ece64 100644 --- a/irc/history/history.go +++ b/irc/history/history.go @@ -22,6 +22,7 @@ const ( Mode Tagmsg Nick + Topic ) const ( From 40d3c59139a2861d15f81ffdaf3073ef352e2e87 Mon Sep 17 00:00:00 2001 From: Shivaram Lingamneni Date: Thu, 23 Apr 2020 01:38:12 -0400 Subject: [PATCH 05/24] fix #955 --- irc/client_lookup_set.go | 11 ++++++++++- irc/errors.go | 1 + irc/nickname.go | 11 +++++------ irc/server.go | 11 +++++++---- 4 files changed, 23 insertions(+), 11 deletions(-) diff --git a/irc/client_lookup_set.go b/irc/client_lookup_set.go index 048b0d3b..6d9834f9 100644 --- a/irc/client_lookup_set.go +++ b/irc/client_lookup_set.go @@ -205,9 +205,18 @@ func (clients *ClientManager) SetNick(client *Client, session *Session, newNick // the client may just be changing case if currentClient != nil && currentClient != client && session != nil { // these conditions forbid reattaching to an existing session: - if registered || !bouncerAllowed || account == "" || account != currentClient.Account() || client.HasMode(modes.TLS) != currentClient.HasMode(modes.TLS) { + if registered || !bouncerAllowed || account == "" || account != currentClient.Account() { return "", errNicknameInUse } + // check TLS modes + if client.HasMode(modes.TLS) != currentClient.HasMode(modes.TLS) { + if useAccountName { + // #955: this is fatal because they can't fix it by trying a different nick + return "", errInsecureReattach + } else { + return "", errNicknameInUse + } + } reattachSuccessful, numSessions, lastSeen := currentClient.AddSession(session) if !reattachSuccessful { return "", errNicknameInUse diff --git a/irc/errors.go b/irc/errors.go index d860e9e2..fc11e320 100644 --- a/irc/errors.go +++ b/irc/errors.go @@ -42,6 +42,7 @@ var ( errNickMissing = errors.New("nick missing") errNicknameInvalid = errors.New("invalid nickname") errNicknameInUse = errors.New("nickname in use") + errInsecureReattach = errors.New("insecure reattach") errNicknameReserved = errors.New("nickname is reserved") errNickAccountMismatch = errors.New(`Your nickname must match your account name; try logging out and logging back in with SASL`) errNoExistingBan = errors.New("Ban does not exist") diff --git a/irc/nickname.go b/irc/nickname.go index a49a2137..59d7beb2 100644 --- a/irc/nickname.go +++ b/irc/nickname.go @@ -25,12 +25,11 @@ var ( restrictedSkeletons = make(map[string]bool) ) -// returns whether the change succeeded or failed -func performNickChange(server *Server, client *Client, target *Client, session *Session, nickname string, rb *ResponseBuffer) bool { +func performNickChange(server *Server, client *Client, target *Client, session *Session, nickname string, rb *ResponseBuffer) error { currentNick := client.Nick() details := target.Details() if details.nick == nickname { - return true + return nil } hadNick := details.nick != "*" origNickMask := details.nickMask @@ -52,7 +51,7 @@ func performNickChange(server *Server, client *Client, target *Client, session * rb.Add(nil, server.name, ERR_UNKNOWNERROR, currentNick, "NICK", fmt.Sprintf(client.t("Could not set or change nickname: %s"), err.Error())) } if err != nil { - return false + return err } message := utils.MakeMessage("") @@ -88,7 +87,7 @@ func performNickChange(server *Server, client *Client, target *Client, session * client.server.monitorManager.AlertAbout(target, true) target.nickTimer.Touch(rb) } // else: these will be deferred to the end of registration (see #572) - return true + return nil } func (server *Server) RandomlyRename(client *Client) { @@ -124,7 +123,7 @@ func fixupNickEqualsAccount(client *Client, rb *ResponseBuffer, config *Config) if !client.registered { return true } - if !performNickChange(client.server, client, client, rb.session, client.AccountName(), rb) { + if performNickChange(client.server, client, client, rb.session, client.AccountName(), rb) != nil { client.server.accounts.Logout(client) nsNotice(rb, client.t("A client is already using that account; try logging out and logging back in with SASL")) return false diff --git a/irc/server.go b/irc/server.go index 63a1d4d8..cc43d0ea 100644 --- a/irc/server.go +++ b/irc/server.go @@ -343,11 +343,14 @@ func (server *Server) tryRegister(c *Client, session *Session) (exiting bool) { } rb := NewResponseBuffer(session) - nickAssigned := performNickChange(server, c, c, session, c.preregNick, rb) + nickError := performNickChange(server, c, c, session, c.preregNick, rb) rb.Send(true) - if !nickAssigned { + if nickError == errInsecureReattach { + c.Quit(c.t("You can't mix secure and insecure connections to this account"), nil) + return true + } else if nickError != nil { c.preregNick = "" - return + return false } if session.client != c { @@ -355,7 +358,7 @@ func (server *Server) tryRegister(c *Client, session *Session) (exiting bool) { // we'll play the reg burst later, on the new goroutine associated with // (thisSession, otherClient). This is to avoid having to transfer state // like nickname, hostname, etc. to show the correct values in the reg burst. - return + return false } // check KLINEs From 98ac900ac3a40c618d32b3c616f642dc87c7775e Mon Sep 17 00:00:00 2001 From: Shivaram Lingamneni Date: Thu, 23 Apr 2020 02:30:31 -0400 Subject: [PATCH 06/24] fixes --- docs/MANUAL.md | 40 +++++++++++----------------------------- 1 file changed, 11 insertions(+), 29 deletions(-) diff --git a/docs/MANUAL.md b/docs/MANUAL.md index a122cbe7..7cc1b6be 100644 --- a/docs/MANUAL.md +++ b/docs/MANUAL.md @@ -124,7 +124,7 @@ If you're using Arch Linux, you can also install the [`oragono` package](https:/ For further information and a sample docker-compose file see the separate [Docker documentation](https://github.com/oragono/oragono/blob/master/distrib/docker/README.md). -## Productionizing on Linux +## Productionizing The recommended way to operate oragono as a service on Linux is via systemd. This provides a standard interface for starting, stopping, and rehashing (via `systemctl reload`) the service. It also captures oragono's loglines (sent to stderr in the default configuration) and writes them to the system journal. @@ -134,11 +134,14 @@ The only major distribution that currently packages Oragono is Arch Linux; the a 1. Copy the executable binary `oragono`, the config file `ircd.yaml`, the database `ircd.db`, and the self-signed TLS certificate (`tls.crt` and `tls.key`) to `/home/oragono`. Ensure that they are all owned by the new oragono role user: `sudo chown oragono:oragono /home/oragono/*`. Ensure that the configuration file logs to stderr. 1. Install our example [oragono.service](https://github.com/oragono/oragono/blob/master/distrib/systemd/oragono.service) file to `/etc/systemd/system/oragono.service`. 1. Enable and start the new service with the following commands: - 1. `systemctl daemon-reload` - 1. `systemctl enable oragono.service` - 1. `systemctl start oragono.service` - 1. Confirm that the service started correctly with `systemctl status oragono.service` -1. Now, if you haven't already, obtain valid TLS certificates for your domain. The simplest way to do this is to get them from [Let's Encrypt](https://letsencrypt.org/) with [Certbot](https://certbot.eff.org/). The correct way to do this will depend on whether you are already running a web server on port 80. If you are, follow the guides on the Certbot website; if you aren't, you can use `certbot certonly --standalone --preferred-challenges http -d example.com` (replace `example.com` with your domain). + 1. `systemctl daemon-reload` + 1. `systemctl enable oragono.service` + 1. `systemctl start oragono.service` + 1. Confirm that the service started correctly with `systemctl status oragono.service` + +The other major hurdle for productionizing (but one well worth the effort) is obtaining valid TLS certificates for your domain, if you haven't already done so: + +1. The simplest way to get valid TLS certificates is from [Let's Encrypt](https://letsencrypt.org/) with [Certbot](https://certbot.eff.org/). The correct procedure will depend on whether you are already running a web server on port 80. If you are, follow the guides on the Certbot website; if you aren't, you can use `certbot certonly --standalone --preferred-challenges http -d example.com` (replace `example.com` with your domain). 1. At this point, you should have certificates available at `/etc/letsencrypt/live/example.com` (replacing `example.com` with your domain). You should serve `fullchain.pem` as the certificate and `privkey.pem` as its private key. However, these files are owned by root and the private key is not readable by the oragono role user, so you won't be able to use them directly in their current locations. You can write a post-renewal hook for certbot to make copies of these certificates accessible to the oragono role user. For example, install the following script as `/etc/letsencrypt/renewal-hooks/post/install-oragono-certificates`, again replacing `example.com` with your domain name, and chmod it 0755: ````bash @@ -393,30 +396,9 @@ Similarly, for a public channel (one without `+i`), users can ban nick/account n # IRC over TLS -IRC has traditionally been available over both plaintext (on port 6667) and SSL/TLS (on port 6697). We recommend that you make your server available exclusively via TLS, since exposing plaintext access allows for unauthorized interception or modification of user data or passwords. While the default config file exposes a plaintext public port, it also contains instructions on how to disable it or replace it with a 'dummy' plaintext listener that simply directs users to reconnect using TLS. - - -## How do I use Let's Encrypt certificates? - -[Let's Encrypt](https://letsencrypt.org) is a widely recognized certificate authority that provides free certificates. Here's a quick-start guide for using those certificates with Oragono: - -1. Follow this [guidance](https://letsencrypt.org/getting-started/) from Let's Encrypt to create your certificates. -2. You should now have a set of `pem` files, Mainly, we're interested in your `live/` Let's Encrypt directory (e.g. `/etc/letsencrypt/live//`). -3. Here are how the config file keys map to LE files: - - `cert: tls.crt` is `live//fullchain.pem` - - ` key: tls.key` is `live//privkey.pem` -4. You may need to copy the `pem` files to another directory so Oragono can read them, or similarly use a script like [this one](https://github.com/darwin-network/slash/blob/master/etc/bin/install-lecerts) to automagically do something similar. -5. By default, `certbot` will automatically renew your certificates. Oragono will only reread certificates when it is restarted, or during a rehash (e.g., on receiving the `/rehash` command or the `SIGHUP` signal). You can add an executable script to `/etc/letsencrypt/renewal-hooks/post` that can perform the rehash. Here's one example of such a script: - -```bash -#!/bin/bash -pkill -HUP oragono -``` - -The main issues you'll run into are going to be permissions issues. This is because by default, certbot will generate certificates that non-root users can't (and probably shouldn't) read. If you run into trouble, look over the script in step **4** and/or make sure you're copying the files to somewhere else, as well as giving them correct permissions with `chown`, `chgrp` and `chmod`. - -On other platforms or with alternative ACME tools, you may need to use other steps or the specific files may be named differently. +IRC has traditionally been available over both plaintext (on port 6667) and SSL/TLS (on port 6697). We recommend that you make your server available exclusively via TLS, since exposing plaintext access allows for unauthorized interception or modification of user data or passwords. The default config file no longer exposes a plaintext port, so if you haven't modified your `listeners` section, you're good to go. +For a quickstart guide to obtaining valid TLS certificates from Let's Encrypt, see the "productionizing" section of the manual above. ## How can I "redirect" users from plaintext to TLS? From 6e0309fa287a004d149272db12822269e6cfa3d7 Mon Sep 17 00:00:00 2001 From: Shivaram Lingamneni Date: Thu, 23 Apr 2020 02:33:31 -0400 Subject: [PATCH 07/24] add a missing detail --- docs/MANUAL.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/MANUAL.md b/docs/MANUAL.md index 7cc1b6be..08677a7d 100644 --- a/docs/MANUAL.md +++ b/docs/MANUAL.md @@ -157,6 +157,8 @@ chown oragono:oragono /home/oragono/tls.* systemctl reload oragono.service ```` +Executing this script manually will install the certificates for the first time and perform a rehash, enabling them. + You can also change the ownership of the files under `/etc/letsencrypt` so that the oragono user can read them, as described in the [UnrealIRCd documentation](https://www.unrealircd.org/docs/Setting_up_certbot_for_use_with_UnrealIRCd#Tweaking_permissions_on_the_key_file), but this is not recommended if you plan to use the certificate for services other than oragono. On a non-systemd system, oragono can be configured to log to a file and used [logrotate(8)](https://linux.die.net/man/8/logrotate), since it will reopen its log files (as well as rehashing the config file) upon receiving a SIGHUP. From f2fec0dc5957207dc2faf988228b051b2da1ac01 Mon Sep 17 00:00:00 2001 From: Shivaram Lingamneni Date: Thu, 23 Apr 2020 02:46:27 -0400 Subject: [PATCH 08/24] remove caveat --- docs/MANUAL.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/MANUAL.md b/docs/MANUAL.md index 08677a7d..949c7249 100644 --- a/docs/MANUAL.md +++ b/docs/MANUAL.md @@ -159,7 +159,7 @@ systemctl reload oragono.service Executing this script manually will install the certificates for the first time and perform a rehash, enabling them. -You can also change the ownership of the files under `/etc/letsencrypt` so that the oragono user can read them, as described in the [UnrealIRCd documentation](https://www.unrealircd.org/docs/Setting_up_certbot_for_use_with_UnrealIRCd#Tweaking_permissions_on_the_key_file), but this is not recommended if you plan to use the certificate for services other than oragono. +If you are using Certbot 0.29.0 or higher, you can also change the ownership of the files under `/etc/letsencrypt` so that the oragono user can read them, as described in the [UnrealIRCd documentation](https://www.unrealircd.org/docs/Setting_up_certbot_for_use_with_UnrealIRCd#Tweaking_permissions_on_the_key_file). On a non-systemd system, oragono can be configured to log to a file and used [logrotate(8)](https://linux.die.net/man/8/logrotate), since it will reopen its log files (as well as rehashing the config file) upon receiving a SIGHUP. From f667c035a949a19a32d587bc77f38db3d0f18531 Mon Sep 17 00:00:00 2001 From: Shivaram Lingamneni Date: Thu, 23 Apr 2020 12:45:42 -0400 Subject: [PATCH 09/24] mention pkill and killall --- docs/MANUAL.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/MANUAL.md b/docs/MANUAL.md index 949c7249..7da3c2c5 100644 --- a/docs/MANUAL.md +++ b/docs/MANUAL.md @@ -161,7 +161,7 @@ Executing this script manually will install the certificates for the first time If you are using Certbot 0.29.0 or higher, you can also change the ownership of the files under `/etc/letsencrypt` so that the oragono user can read them, as described in the [UnrealIRCd documentation](https://www.unrealircd.org/docs/Setting_up_certbot_for_use_with_UnrealIRCd#Tweaking_permissions_on_the_key_file). -On a non-systemd system, oragono can be configured to log to a file and used [logrotate(8)](https://linux.die.net/man/8/logrotate), since it will reopen its log files (as well as rehashing the config file) upon receiving a SIGHUP. +On a non-systemd system, oragono can be configured to log to a file and used [logrotate(8)](https://linux.die.net/man/8/logrotate), since it will reopen its log files (as well as rehashing the config file) upon receiving a SIGHUP. To send the sighup, you can use `killall -HUP oragono` or `pkill -HUP oragono`. ## Upgrading to a new version of Oragono From b065a9aefe35d8e416348507f05a0c8bd7f89187 Mon Sep 17 00:00:00 2001 From: Shivaram Lingamneni Date: Thu, 23 Apr 2020 12:49:45 -0400 Subject: [PATCH 10/24] clarify manual correction --- docs/MANUAL.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/MANUAL.md b/docs/MANUAL.md index 7da3c2c5..733441d8 100644 --- a/docs/MANUAL.md +++ b/docs/MANUAL.md @@ -161,7 +161,7 @@ Executing this script manually will install the certificates for the first time If you are using Certbot 0.29.0 or higher, you can also change the ownership of the files under `/etc/letsencrypt` so that the oragono user can read them, as described in the [UnrealIRCd documentation](https://www.unrealircd.org/docs/Setting_up_certbot_for_use_with_UnrealIRCd#Tweaking_permissions_on_the_key_file). -On a non-systemd system, oragono can be configured to log to a file and used [logrotate(8)](https://linux.die.net/man/8/logrotate), since it will reopen its log files (as well as rehashing the config file) upon receiving a SIGHUP. To send the sighup, you can use `killall -HUP oragono` or `pkill -HUP oragono`. +On a non-systemd system, oragono can be configured to log to a file and used [logrotate(8)](https://linux.die.net/man/8/logrotate), since it will reopen its log files (as well as rehashing the config file) upon receiving a SIGHUP. To rehash manually outside the context of log rotation, you can use `killall -HUP oragono` or `pkill -HUP oragono`. ## Upgrading to a new version of Oragono From 4722f8a96c652651fc80d5c4011e20d7d99281ca Mon Sep 17 00:00:00 2001 From: Shivaram Lingamneni Date: Fri, 24 Apr 2020 01:33:21 -0400 Subject: [PATCH 11/24] fix #959 --- irc/channel.go | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/irc/channel.go b/irc/channel.go index c9adaf09..c18eddc7 100644 --- a/irc/channel.go +++ b/irc/channel.go @@ -1284,13 +1284,16 @@ func (channel *Channel) SendSplitMessage(command string, minPrefixMode modes.Mod } } - channel.AddHistoryItem(history.Item{ - Type: histType, - Message: message, - Nick: nickmask, - AccountName: account, - Tags: clientOnlyTags, - }) + // #959: don't save STATUSMSG + if minPrefixMode == modes.Mode(0) { + channel.AddHistoryItem(history.Item{ + Type: histType, + Message: message, + Nick: nickmask, + AccountName: account, + Tags: clientOnlyTags, + }) + } } func (channel *Channel) applyModeToMember(client *Client, change modes.ModeChange, rb *ResponseBuffer) (applied bool, result modes.ModeChange) { From f87b71b93f348565b8833e4ebe17968eb6b14920 Mon Sep 17 00:00:00 2001 From: Shivaram Lingamneni Date: Fri, 24 Apr 2020 15:39:39 -0400 Subject: [PATCH 12/24] fix #962 --- irc/accounts.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/irc/accounts.go b/irc/accounts.go index 9926c900..68a8b7a6 100644 --- a/irc/accounts.go +++ b/irc/accounts.go @@ -1174,7 +1174,12 @@ func (am *AccountManager) Unregister(account string, erase bool) error { var channelsStr string keepProtections := false am.server.store.Update(func(tx *buntdb.Tx) error { + // get the unfolded account name; for an active account, this is + // stored under accountNameKey, for an unregistered account under unregisteredKey accountName, _ = tx.Get(accountNameKey) + if accountName == "" { + accountName, _ = tx.Get(unregisteredKey) + } if erase { tx.Delete(unregisteredKey) } else { From a7f020320e6a2a7ef135979bc9821e395e033549 Mon Sep 17 00:00:00 2001 From: Shivaram Lingamneni Date: Fri, 24 Apr 2020 15:41:58 -0400 Subject: [PATCH 13/24] make erase confirmation code nondeterministic --- irc/nickserv.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/irc/nickserv.go b/irc/nickserv.go index 4ea051c0..1f52a5df 100644 --- a/irc/nickserv.go +++ b/irc/nickserv.go @@ -841,6 +841,8 @@ func nsUnregisterHandler(server *Server, client *Client, command string, params if erase { // account may not be in a loadable state, e.g., if it was unregistered accountName = username + // make the confirmation code nondeterministic for ERASE + registeredAt = server.ctime } else { account, err := server.accounts.LoadAccount(username) if err == errAccountDoesNotExist { From 1c946e19046217b7521dbf7626462dee4d998c0f Mon Sep 17 00:00:00 2001 From: Daniel Oaks Date: Sat, 25 Apr 2020 16:04:57 +1000 Subject: [PATCH 14/24] Fix incorrect /NS example (thanks KindOne!) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 124af7cb..3a44f475 100644 --- a/README.md +++ b/README.md @@ -112,7 +112,7 @@ oragono run ### How to register a channel -1. Register your account with `/NS REGISTER ` +1. Register your account with `/NS REGISTER ` 2. Join the channel with `/join #channel` 3. Register the channel with `/CS REGISTER #channel` From 57e21877420bb916d167ff807cce9f950f230815 Mon Sep 17 00:00:00 2001 From: Shivaram Lingamneni Date: Sun, 26 Apr 2020 02:19:10 -0400 Subject: [PATCH 15/24] fix #964 --- conventional.yaml | 4 ++++ irc/config.go | 1 + irc/handlers.go | 8 ++++++++ oragono.yaml | 4 ++++ 4 files changed, 17 insertions(+) diff --git a/conventional.yaml b/conventional.yaml index 0bba476a..5da77654 100644 --- a/conventional.yaml +++ b/conventional.yaml @@ -491,6 +491,10 @@ channels: # how many channels can each account register? max-channels-per-account: 15 + # as a crude countermeasure against spambots, connections younger than this value + # will get an empty response to /LIST (a time period of 0 disables) + list-delay: 0s + # operator classes oper-classes: # local operator diff --git a/irc/config.go b/irc/config.go index d2c20c58..42d58512 100644 --- a/irc/config.go +++ b/irc/config.go @@ -550,6 +550,7 @@ type Config struct { OperatorOnly bool `yaml:"operator-only"` MaxChannelsPerAccount int `yaml:"max-channels-per-account"` } + ListDelay time.Duration `yaml:"list-delay"` } OperClasses map[string]*OperClassConfig `yaml:"oper-classes"` diff --git a/irc/handlers.go b/irc/handlers.go index 86af4a1e..711df3a4 100644 --- a/irc/handlers.go +++ b/irc/handlers.go @@ -1408,6 +1408,14 @@ func languageHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb * // LIST [{,}] [{,}] func listHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *ResponseBuffer) bool { + config := server.Config() + if time.Since(client.ctime) < config.Channels.ListDelay && client.Account() == "" { + remaining := time.Until(client.ctime.Add(config.Channels.ListDelay)) + csNotice(rb, fmt.Sprintf(client.t("This server requires that you wait %v after connecting before you can use /LIST. You have %v left."), config.Channels.ListDelay, remaining)) + rb.Add(nil, server.name, RPL_LISTEND, client.nick, client.t("End of LIST")) + return false + } + // get channels var channels []string for _, param := range msg.Params { diff --git a/oragono.yaml b/oragono.yaml index d53798b5..030d46db 100644 --- a/oragono.yaml +++ b/oragono.yaml @@ -512,6 +512,10 @@ channels: # how many channels can each account register? max-channels-per-account: 15 + # as a crude countermeasure against spambots, connections younger than this value + # will get an empty response to /LIST (a time period of 0 disables) + list-delay: 0s + # operator classes oper-classes: # local operator From 3e2138db4fa22db4de231dfcc8b53083ba6c8178 Mon Sep 17 00:00:00 2001 From: Shivaram Lingamneni Date: Sun, 26 Apr 2020 03:00:00 -0400 Subject: [PATCH 16/24] clarify that list-delay only applies to anonymous users --- conventional.yaml | 4 ++-- oragono.yaml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/conventional.yaml b/conventional.yaml index 5da77654..e3929a83 100644 --- a/conventional.yaml +++ b/conventional.yaml @@ -491,8 +491,8 @@ channels: # how many channels can each account register? max-channels-per-account: 15 - # as a crude countermeasure against spambots, connections younger than this value - # will get an empty response to /LIST (a time period of 0 disables) + # as a crude countermeasure against spambots, anonymous connections younger + # than this value will get an empty response to /LIST (a time period of 0 disables) list-delay: 0s # operator classes diff --git a/oragono.yaml b/oragono.yaml index 030d46db..9ae504ab 100644 --- a/oragono.yaml +++ b/oragono.yaml @@ -512,8 +512,8 @@ channels: # how many channels can each account register? max-channels-per-account: 15 - # as a crude countermeasure against spambots, connections younger than this value - # will get an empty response to /LIST (a time period of 0 disables) + # as a crude countermeasure against spambots, anonymous connections younger + # than this value will get an empty response to /LIST (a time period of 0 disables) list-delay: 0s # operator classes From 5cdb81e1ea3be106469572f70adb8a2d2e58528d Mon Sep 17 00:00:00 2001 From: Shivaram Lingamneni Date: Sun, 26 Apr 2020 03:08:44 -0400 Subject: [PATCH 17/24] use Nick() --- irc/handlers.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/irc/handlers.go b/irc/handlers.go index 711df3a4..2933cc50 100644 --- a/irc/handlers.go +++ b/irc/handlers.go @@ -1412,7 +1412,7 @@ func listHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Resp if time.Since(client.ctime) < config.Channels.ListDelay && client.Account() == "" { remaining := time.Until(client.ctime.Add(config.Channels.ListDelay)) csNotice(rb, fmt.Sprintf(client.t("This server requires that you wait %v after connecting before you can use /LIST. You have %v left."), config.Channels.ListDelay, remaining)) - rb.Add(nil, server.name, RPL_LISTEND, client.nick, client.t("End of LIST")) + rb.Add(nil, server.name, RPL_LISTEND, client.Nick(), client.t("End of LIST")) return false } From 47f23b957102405ee636ad264335118f4ccbc1b2 Mon Sep 17 00:00:00 2001 From: Daniel Oaks Date: Sun, 26 Apr 2020 20:41:51 +1000 Subject: [PATCH 18/24] New translations irc.lang.json (Romanian) --- languages/ro-RO-irc.lang.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/languages/ro-RO-irc.lang.json b/languages/ro-RO-irc.lang.json index 27d465ae..da024063 100644 --- a/languages/ro-RO-irc.lang.json +++ b/languages/ro-RO-irc.lang.json @@ -113,7 +113,7 @@ "Former Core Developers:": "Foști dezvoltatori:", "Founder: %s": "Fondator: %s", "GHOSTed by %s": "%s a folosit GHOST", - "Given current server settings, the channel history setting is: %s": "", + "Given current server settings, the channel history setting is: %s": "Conform setărilor actuale ale serverului, setarea pentru istoricul de canal este: %s", "Given current server settings, your client is always-on": "Conform setărilor actuale ale serverului, clientul tău are activă opțiunea de conectare permanentă", "Given current server settings, your client is not always-on": "Conform setărilor actuale ale serverului, clientul tău nu are activă opțiunea de conectare permanentă", "Given current server settings, your direct message history setting is: %s": "Conform setărilor actuale ale serverului, setarea aferentă istoricului de mesaje este: %s", @@ -228,7 +228,7 @@ "Successfully ungrouped nick %s with your account": "Pseudonimul %s a fost degrupat de la contul tău, cu succes", "Successfully unpurged channel %s from the server": "Canalul %s nu mai este purjat din server", "Successfully unregistered account %s": "Contul %s a fost șters cu succes", - "That account is set to always-on; try logging out and logging back in with SASL": "", + "That account is set to always-on; try logging out and logging back in with SASL": "Acel cont are activă setarea conectare-permanentă; încearcă să te deconectezi și să te reconectezi cu SASL", "That certificate fingerprint is already associated with another account": "Amprenta de certificat este deja asociată unui alt cont", "That certificate fingerprint was already authorized": "Amprenta certificatului a fost autorizată deja", "That channel is not registered": "Acel canal nu este înregistrat", From c4d32e8af33fd69511c5a6e3fc513327cf05183d Mon Sep 17 00:00:00 2001 From: Shivaram Lingamneni Date: Sun, 26 Apr 2020 07:11:52 -0400 Subject: [PATCH 19/24] make readme links https --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 3a44f475..8d2a5b35 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Oragono is a modern IRC server written in Go. Its core design principles are: * Being simple to set up and use * Combining the features of an ircd, a services framework, and a bouncer (integrated account management, history storage, and bouncer functionality) -* Bleeding-edge [IRCv3 support](http://ircv3.net/software/servers.html), suitable for use as an IRCv3 reference implementation +* Bleeding-edge [IRCv3 support](https://ircv3.net/software/servers.html), suitable for use as an IRCv3 reference implementation * Highly customizable via a rehashable (i.e., reloadable at runtime) YAML config Oragono is a fork of the [Ergonomadic](https://github.com/jlatt/ergonomadic) IRC daemon <3 @@ -39,7 +39,7 @@ If you want to take a look at a running Oragono instance or test some client cod * automated client connection limits * passwords stored with [bcrypt](https://godoc.org/golang.org/x/crypto) * banning ips/nets and masks with `KLINE` and `DLINE` -* [IRCv3 support](http://ircv3.net/software/servers.html) +* [IRCv3 support](https://ircv3.net/software/servers.html) * a heavy focus on developing with [specifications](https://oragono.io/specs.html) ## Installation From 3626958f1e998ef9c437c4aa7a10a28cfabdf1ff Mon Sep 17 00:00:00 2001 From: Shivaram Lingamneni Date: Mon, 27 Apr 2020 00:58:48 -0400 Subject: [PATCH 20/24] also exempt operators from LIST restrictions --- irc/handlers.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/irc/handlers.go b/irc/handlers.go index 2933cc50..dbcb6793 100644 --- a/irc/handlers.go +++ b/irc/handlers.go @@ -1409,7 +1409,7 @@ func languageHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb * // LIST [{,}] [{,}] func listHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *ResponseBuffer) bool { config := server.Config() - if time.Since(client.ctime) < config.Channels.ListDelay && client.Account() == "" { + if time.Since(client.ctime) < config.Channels.ListDelay && client.Account() == "" && !client.HasMode(modes.Operator) { remaining := time.Until(client.ctime.Add(config.Channels.ListDelay)) csNotice(rb, fmt.Sprintf(client.t("This server requires that you wait %v after connecting before you can use /LIST. You have %v left."), config.Channels.ListDelay, remaining)) rb.Add(nil, server.name, RPL_LISTEND, client.Nick(), client.t("End of LIST")) From df9bf15f0017a0e50d5762864ea123fc6575791e Mon Sep 17 00:00:00 2001 From: Alex Jaspersen Date: Thu, 30 Apr 2020 03:43:55 +0000 Subject: [PATCH 21/24] Add support for setting user modes by default. --- irc/client.go | 4 ++++ irc/config.go | 4 ++++ irc/modes.go | 34 ++++++++++++++++++++++++++-------- oragono.yaml | 6 ++++++ 4 files changed, 40 insertions(+), 8 deletions(-) diff --git a/irc/client.go b/irc/client.go index 1443d6f7..a50959c3 100644 --- a/irc/client.go +++ b/irc/client.go @@ -318,6 +318,10 @@ func (server *Server) RunClient(conn clientConn, proxyLine string) { session.idletimer.Initialize(session) session.resetFakelag() + for _, defaultMode := range config.Accounts.defaultUserModes { + client.SetMode(defaultMode, true) + } + if conn.Config.TLSConfig != nil { client.SetMode(modes.TLS, true) // error is not useful to us here anyways so we can ignore it diff --git a/irc/config.go b/irc/config.go index 42d58512..e40f04cc 100644 --- a/irc/config.go +++ b/irc/config.go @@ -261,6 +261,8 @@ type AccountConfig struct { Exempted []string exemptedNets []net.IPNet } `yaml:"require-sasl"` + DefaultUserModes *string `yaml:"default-user-modes"` + defaultUserModes modes.Modes LDAP ldap.ServerConfig LoginThrottling ThrottleConfig `yaml:"login-throttling"` SkipServerPassword bool `yaml:"skip-server-password"` @@ -982,6 +984,8 @@ func LoadConfig(filename string) (config *Config, err error) { } } + config.Accounts.defaultUserModes = ParseDefaultUserModes(config.Accounts.DefaultUserModes) + config.Accounts.RequireSasl.exemptedNets, err = utils.ParseNetList(config.Accounts.RequireSasl.Exempted) if err != nil { return nil, fmt.Errorf("Could not parse require-sasl exempted nets: %v", err.Error()) diff --git a/irc/modes.go b/irc/modes.go index e6f2c04e..4372f9bb 100644 --- a/irc/modes.go +++ b/irc/modes.go @@ -20,6 +20,10 @@ var ( DefaultChannelModes = modes.Modes{ modes.NoOutside, modes.OpOnlyTopic, } + + // DefaultUserModes are set on all users when they login. + // this can be overridden in the `server` config, with the `default-user-modes` key + DefaultUserModes = modes.Modes{} ) // ApplyUserModeChanges applies the given changes, and returns the applied changes. @@ -102,21 +106,35 @@ func ApplyUserModeChanges(client *Client, changes modes.ModeChanges, force bool, return applied } +// parseDefaultModes uses the provided mode change parser to parse the rawModes. +func parseDefaultModes(rawModes string, parser func(params ...string) (modes.ModeChanges, map[rune]bool)) modes.Modes { + modeChangeStrings := strings.Fields(rawModes) + modeChanges, _ := parser(modeChangeStrings...) + defaultModes := make(modes.Modes, 0) + for _, modeChange := range modeChanges { + if modeChange.Op == modes.Add { + defaultModes = append(defaultModes, modeChange.Mode) + } + } + return defaultModes +} + // ParseDefaultChannelModes parses the `default-modes` line of the config func ParseDefaultChannelModes(rawModes *string) modes.Modes { if rawModes == nil { // not present in config, fall back to compile-time default return DefaultChannelModes } - modeChangeStrings := strings.Fields(*rawModes) - modeChanges, _ := modes.ParseChannelModeChanges(modeChangeStrings...) - defaultChannelModes := make(modes.Modes, 0) - for _, modeChange := range modeChanges { - if modeChange.Op == modes.Add { - defaultChannelModes = append(defaultChannelModes, modeChange.Mode) - } + return parseDefaultModes(*rawModes, modes.ParseChannelModeChanges) +} + +// ParseDefaultUserModes parses the `default-user-modes` line of the config +func ParseDefaultUserModes(rawModes *string) modes.Modes { + if rawModes == nil { + // not present in config, fall back to compile-time default + return DefaultUserModes } - return defaultChannelModes + return parseDefaultModes(*rawModes, modes.ParseUserModeChanges) } // ApplyChannelModeChanges applies a given set of mode changes. diff --git a/oragono.yaml b/oragono.yaml index 9ae504ab..9bf309e5 100644 --- a/oragono.yaml +++ b/oragono.yaml @@ -451,6 +451,12 @@ accounts: offer-list: #- "oragono.test" + # modes that are set by default when a user connects + # if unset, no user modes will be set by default + # +i is invisible (a user's channels are hidden from whois replies) + # see /QUOTE HELP umodes for more user modes + # default-user-modes: +i + # support for deferring password checking to an external LDAP server # you should probably ignore this section! consult the grafana docs for details: # https://grafana.com/docs/grafana/latest/auth/ldap/ From 52910a185c1e8e2b1f724e1a00c8f6ae7d968632 Mon Sep 17 00:00:00 2001 From: Alex Jaspersen Date: Thu, 30 Apr 2020 03:53:33 +0000 Subject: [PATCH 22/24] Add test for ParseDefaultUserModes. --- irc/modes_test.go | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/irc/modes_test.go b/irc/modes_test.go index ece33313..005d0555 100644 --- a/irc/modes_test.go +++ b/irc/modes_test.go @@ -35,6 +35,31 @@ func TestParseDefaultChannelModes(t *testing.T) { } } +func TestParseDefaultUserModes(t *testing.T) { + iR := "+iR" + i := "+i" + empty := "" + rminusi := "+R -i" + + var parseTests = []struct { + raw *string + expected modes.Modes + }{ + {&iR, modes.Modes{modes.Invisible, modes.RegisteredOnly}}, + {&i, modes.Modes{modes.Invisible}}, + {&empty, modes.Modes{}}, + {&rminusi, modes.Modes{modes.RegisteredOnly}}, + {nil, modes.Modes{}}, + } + + for _, testcase := range parseTests { + result := ParseDefaultUserModes(testcase.raw) + if !reflect.DeepEqual(result, testcase.expected) { + t.Errorf("expected modes %s, got %s", testcase.expected, result) + } + } +} + func TestUmodeGreaterThan(t *testing.T) { if !umodeGreaterThan(modes.Halfop, modes.Voice) { t.Errorf("expected Halfop > Voice") From b3a7e5c996904ac8dad24b6784f5db4a84510c2f Mon Sep 17 00:00:00 2001 From: Alex Jaspersen Date: Thu, 30 Apr 2020 04:38:19 +0000 Subject: [PATCH 23/24] Set default user modes when always-on clients reconnect. Add default-user-modes configuration to conventional.yaml. Fix comment on DefaultUserModes. --- conventional.yaml | 6 ++++++ irc/client.go | 4 ++++ irc/modes.go | 2 +- 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/conventional.yaml b/conventional.yaml index e3929a83..0c9df059 100644 --- a/conventional.yaml +++ b/conventional.yaml @@ -430,6 +430,12 @@ accounts: offer-list: #- "oragono.test" + # modes that are set by default when a user connects + # if unset, no user modes will be set by default + # +i is invisible (a user's channels are hidden from whois replies) + # see /QUOTE HELP umodes for more user modes + # default-user-modes: +i + # support for deferring password checking to an external LDAP server # you should probably ignore this section! consult the grafana docs for details: # https://grafana.com/docs/grafana/latest/auth/ldap/ diff --git a/irc/client.go b/irc/client.go index a50959c3..b636678a 100644 --- a/irc/client.go +++ b/irc/client.go @@ -375,6 +375,10 @@ func (server *Server) AddAlwaysOnClient(account ClientAccount, chnames []string, alwaysOn: true, } + for _, defaultMode := range config.Accounts.defaultUserModes { + client.SetMode(defaultMode, true) + } + client.SetMode(modes.TLS, true) client.writerSemaphore.Initialize(1) client.history.Initialize(0, 0) diff --git a/irc/modes.go b/irc/modes.go index 4372f9bb..86912474 100644 --- a/irc/modes.go +++ b/irc/modes.go @@ -22,7 +22,7 @@ var ( } // DefaultUserModes are set on all users when they login. - // this can be overridden in the `server` config, with the `default-user-modes` key + // this can be overridden in the `accounts` config, with the `default-user-modes` key DefaultUserModes = modes.Modes{} ) From 4548eee6f1570c88173442cfb581ecf03c2d6df3 Mon Sep 17 00:00:00 2001 From: Shivaram Lingamneni Date: Thu, 30 Apr 2020 01:47:58 -0400 Subject: [PATCH 24/24] make +i the recommended default usermode --- oragono.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/oragono.yaml b/oragono.yaml index 9bf309e5..cb75b0ef 100644 --- a/oragono.yaml +++ b/oragono.yaml @@ -455,7 +455,7 @@ accounts: # if unset, no user modes will be set by default # +i is invisible (a user's channels are hidden from whois replies) # see /QUOTE HELP umodes for more user modes - # default-user-modes: +i + default-user-modes: +i # support for deferring password checking to an external LDAP server # you should probably ignore this section! consult the grafana docs for details: