@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator

Raise a setup warning when locked configuration has a configuration value stored in the database

Summary:
Ref T13249. See <https://discourse.phabricator-community.org/t/configuring-the-number-of-taskmaster-daemons/2394/>.

Today, when a configuration value is "locked", we prevent //writes// to the database. However, we still perform reads. When you upgrade, we generally don't want a bunch of your configuration to change by surprise.

Some day, I'd like to stop reading locked configuration from the database. This would defuse an escalation where an attacker finds a way to write to locked configuration despite safeguards, e.g. through SQL injection or policy bypass. Today, they could write to `cluster.mailers` or similar and substantially escalate access. A better behavior would be to ignore database values for `cluster.mailers` and other locked config, so that these impermissible writes have no effect.

Doing this today would break a lot of installs, but we can warn them about it now and then make the change at a later date.

Test Plan:
- Forced a `phd.taskmasters` config value into the database.
- Saw setup warning.
- Used `bin/config delete --database phd.taskmasters` to clear the warning.
- Reviewed documentation changes.
- Reviewed `phd.taskmasters` documentation adjustment.

Reviewers: amckinley

Reviewed By: amckinley

Maniphest Tasks: T13249

Differential Revision: https://secure.phabricator.com/D20159

+153 -4
+98 -3
src/applications/config/check/PhabricatorExtraConfigSetupCheck.php
··· 15 15 16 16 $defined_keys = PhabricatorApplicationConfigOptions::loadAllOptions(); 17 17 18 + $stack = PhabricatorEnv::getConfigSourceStack(); 19 + $stack = $stack->getStack(); 20 + 18 21 foreach ($all_keys as $key) { 19 22 if (isset($defined_keys[$key])) { 20 23 continue; ··· 48 51 ->setName($name) 49 52 ->setSummary($summary); 50 53 51 - $stack = PhabricatorEnv::getConfigSourceStack(); 52 - $stack = $stack->getStack(); 53 - 54 54 $found = array(); 55 55 $found_local = false; 56 56 $found_database = false; ··· 85 85 } 86 86 } 87 87 88 + $options = PhabricatorApplicationConfigOptions::loadAllOptions(); 89 + foreach ($defined_keys as $key => $value) { 90 + $option = idx($options, $key); 91 + if (!$option) { 92 + continue; 93 + } 94 + 95 + if (!$option->getLocked()) { 96 + continue; 97 + } 98 + 99 + $found_database = false; 100 + foreach ($stack as $source_key => $source) { 101 + $value = $source->getKeys(array($key)); 102 + if ($value) { 103 + if ($source instanceof PhabricatorConfigDatabaseSource) { 104 + $found_database = true; 105 + break; 106 + } 107 + } 108 + } 109 + 110 + if (!$found_database) { 111 + continue; 112 + } 113 + 114 + // NOTE: These are values which we don't let you edit directly, but edit 115 + // via other UI workflows. For now, don't raise this warning about them. 116 + // In the future, before we stop reading database configuration for 117 + // locked values, we either need to add a flag which lets these values 118 + // continue reading from the database or move them to some other storage 119 + // mechanism. 120 + $soft_locks = array( 121 + 'phabricator.uninstalled-applications', 122 + 'phabricator.application-settings', 123 + 'config.ignore-issues', 124 + ); 125 + $soft_locks = array_fuse($soft_locks); 126 + if (isset($soft_locks[$key])) { 127 + continue; 128 + } 129 + 130 + $doc_name = 'Configuration Guide: Locked and Hidden Configuration'; 131 + $doc_href = PhabricatorEnv::getDoclink($doc_name); 132 + 133 + $set_command = phutil_tag( 134 + 'tt', 135 + array(), 136 + csprintf( 137 + 'bin/config set %R <value>', 138 + $key)); 139 + 140 + $summary = pht( 141 + 'Configuration value "%s" is locked, but has a value in the database.', 142 + $key); 143 + $message = pht( 144 + 'The configuration value "%s" is locked (so it can not be edited '. 145 + 'from the web UI), but has a database value. Usually, this means '. 146 + 'that it was previously not locked, you set it using the web UI, '. 147 + 'and it later became locked.'. 148 + "\n\n". 149 + 'You should copy this configuration value in a local configuration '. 150 + 'source (usually by using %s) and then remove it from the database '. 151 + 'with the command below.'. 152 + "\n\n". 153 + 'For more information on locked and hidden configuration, including '. 154 + 'details about this setup issue, see %s.'. 155 + "\n\n". 156 + 'This database value is currently respected, but a future version '. 157 + 'of Phabricator will stop respecting database values for locked '. 158 + 'configuration options.', 159 + $key, 160 + $set_command, 161 + phutil_tag( 162 + 'a', 163 + array( 164 + 'href' => $doc_href, 165 + 'target' => '_blank', 166 + ), 167 + $doc_name)); 168 + $command = csprintf( 169 + 'phabricator/ $ ./bin/config delete --database %R', 170 + $key); 171 + 172 + $this->newIssue('config.locked.'.$key) 173 + ->setShortName(pht('Deprecated Config Source')) 174 + ->setName( 175 + pht( 176 + 'Locked Configuration Option "%s" Has Database Value', 177 + $key)) 178 + ->setSummary($summary) 179 + ->setMessage($message) 180 + ->addCommand($command) 181 + ->addPhabricatorConfig($key); 182 + } 88 183 89 184 if (PhabricatorEnv::getEnvConfig('feed.http-hooks')) { 90 185 $this->newIssue('config.deprecated.feed.http-hooks')
+6 -1
src/applications/config/option/PhabricatorPHDConfigOptions.php
··· 41 41 "If you are running a cluster, this limit applies separately ". 42 42 "to each instance of `phd`. For example, if this limit is set ". 43 43 "to `4` and you have three hosts running daemons, the effective ". 44 - "global limit will be 12.")), 44 + "global limit will be 12.". 45 + "\n\n". 46 + "After changing this value, you must restart the daemons. Most ". 47 + "configuration changes are picked up by the daemons ". 48 + "automatically, but pool sizes can not be changed without a ". 49 + "restart.")), 45 50 $this->newOption('phd.verbose', 'bool', false) 46 51 ->setLocked(true) 47 52 ->setBoolOptions(
+49
src/docs/user/configuration/configuration_locked.diviner
··· 111 111 ``` 112 112 113 113 114 + Locked Configuration With Database Values 115 + ========================================= 116 + 117 + You may receive a setup issue warning you that a locked configuration key has a 118 + value set in the database. Most commonly, this is because: 119 + 120 + - In some earlier version of Phabricator, this configuration was not locked. 121 + - In the past, you or some other administrator used the web UI to set a 122 + value. This value was written to the database. 123 + - In a later version of the software, the value became locked. 124 + 125 + When Phabricator was originally released, locked configuration did not yet 126 + exist. Locked configuration was introduced later, and then configuration options 127 + were gradually locked for a long time after that. 128 + 129 + In some cases the meaning of a value changed and it became possible to use it 130 + to break an install or the configuration became a security risk. In other 131 + cases, we identified an existing security risk or arrived at some other reason 132 + to lock the value. 133 + 134 + Locking values was more common in the past, and it is now relatively rare for 135 + an unlocked value to become locked: when new values are introduced, they are 136 + generally locked or hidden appropriately. In most cases, this setup issue only 137 + affects installs that have used Phabricator for a long time. 138 + 139 + At time of writing (February 2019), Phabricator currently respects these old 140 + database values. However, some future version of Phabricator will refuse to 141 + read locked configuration from the database, because this improves security if 142 + an attacker manages to find a way to bypass restrictions on editing locked 143 + configuration from the web UI. 144 + 145 + To clear this setup warning and avoid surprise behavioral changes in the future, 146 + you should move these configuration values from the database to a local config 147 + file. Usually, you'll do this by first copying the value from the database: 148 + 149 + ``` 150 + phabricator/ $ ./bin/config set <key> <value> 151 + ``` 152 + 153 + ...and then removing the database value: 154 + 155 + ``` 156 + phabricator/ $ ./bin/config delete --database <key> 157 + ``` 158 + 159 + See @{Configuration User Guide: Advanced Configuration} for some more detailed 160 + discussion of different configuration sources. 161 + 162 + 114 163 Next Steps 115 164 ========== 116 165