Mercurial > p > roundup > code
comparison roundup/configuration.py @ 7809:be6cb2e0d471
feat: add support for rotating jwt keys
This allows jwt_secret to have multiple ',' separated secrets. The
first/leftmost should be used to sign new JWTs. All of them are used
(starting from left/newest) to try to verify a JWT.
If the first secret is < 32 chars in length JWTs are disabled. If any
of the other secrets are < 32 chars, the configuration code causes the
software to exit. This prevents insecure (too short) secrets from
being used.
Updated doc examples and tests.
| author | John Rouillard <rouilj@ieee.org> |
|---|---|
| date | Thu, 14 Mar 2024 19:04:19 -0400 |
| parents | 6f66d74d37f3 |
| children | 890097bc4cd0 |
comparison
equal
deleted
inserted
replaced
| 7808:6c5f8da9fca7 | 7809:be6cb2e0d471 |
|---|---|
| 824 class SecretNullableOption(NullableOption, SecretOption): | 824 class SecretNullableOption(NullableOption, SecretOption): |
| 825 # use get from SecretOption and rest from NullableOption | 825 # use get from SecretOption and rest from NullableOption |
| 826 get = SecretOption.get | 826 get = SecretOption.get |
| 827 class_description = SecretOption.class_description | 827 class_description = SecretOption.class_description |
| 828 | 828 |
| 829 class ListSecretOption(SecretOption): | |
| 830 # use get from SecretOption | |
| 831 def get(self): | |
| 832 value = SecretOption.get(self) | |
| 833 return [x.lstrip() for x in value.split(',')] | |
| 834 | |
| 835 class_description = SecretOption.class_description | |
| 836 | |
| 837 def validate(self, options): | |
| 838 if self.name == "WEB_JWT_SECRET": | |
| 839 secrets = self.get() | |
| 840 invalid_secrets = [ x for x in secrets[1:] if len(x) < 32] | |
| 841 if invalid_secrets: | |
| 842 raise OptionValueError( | |
| 843 self, ", ".join(secrets), | |
| 844 "One or more secrets less then 32 characters in length\n" | |
| 845 "found: %s" % ', '.join(invalid_secrets)) | |
| 846 else: | |
| 847 self.get() | |
| 829 | 848 |
| 830 class RedisUrlOption(SecretNullableOption): | 849 class RedisUrlOption(SecretNullableOption): |
| 831 """Do required check to make sure known bad parameters are not | 850 """Do required check to make sure known bad parameters are not |
| 832 put in the url. | 851 put in the url. |
| 833 | 852 |
| 1435 "Changing this changes the etag and invalidates updates by\n" | 1454 "Changing this changes the etag and invalidates updates by\n" |
| 1436 "clients. It must be persistent across application restarts.\n" | 1455 "clients. It must be persistent across application restarts.\n" |
| 1437 "(Note the default value changes every time\n" | 1456 "(Note the default value changes every time\n" |
| 1438 " roundup-admin updateconfig\n" | 1457 " roundup-admin updateconfig\n" |
| 1439 "is run, so it must be explicitly set to a non-empty string.\n"), | 1458 "is run, so it must be explicitly set to a non-empty string.\n"), |
| 1440 (SecretNullableOption, "jwt_secret", "disabled", | 1459 (ListSecretOption, "jwt_secret", "disabled", |
| 1441 "This is used to generate/validate json web tokens (jwt).\n" | 1460 "This is used to sign/validate json web tokens\n" |
| 1442 "Even if you don't use jwts it must not be empty.\n" | 1461 "(JWT). Even if you don't use JWTs it must not be\n" |
| 1443 "If less than 256 bits (32 characters) in length it will\n" | 1462 "empty. You can use multiple secrets separated by a\n" |
| 1444 "disable use of jwt. Changing this invalidates all jwts\n" | 1463 "comma ','. This allows for secret rotation. The newest\n" |
| 1445 "issued by the roundup instance requiring *all* users to\n" | 1464 "secret should be placed first and used for signing. The\n" |
| 1446 "generate new jwts. This is experimental and disabled by\n" | 1465 "rest of the secrets are used for validating an old JWT.\n" |
| 1447 "default. It must be persistent across application restarts.\n"), | 1466 "If the first secret is less than 256 bits (32\n" |
| 1467 "characters) in length JWTs are disabled. If other secrets\n" | |
| 1468 "are less than 32 chars, the application will exit. Removing\n" | |
| 1469 "a secret from this list invalidates all JWTs signed with\n" | |
| 1470 "the secret. JWT support is experimental and disabled by\n" | |
| 1471 "default. The secrets must be persistent across\n" | |
| 1472 "application restarts.\n"), | |
| 1448 )), | 1473 )), |
| 1449 ("rdbms", ( | 1474 ("rdbms", ( |
| 1450 (DatabaseBackend, 'backend', NODEFAULT, | 1475 (DatabaseBackend, 'backend', NODEFAULT, |
| 1451 "Database backend."), | 1476 "Database backend."), |
| 1452 (Option, 'name', 'roundup', | 1477 (Option, 'name', 'roundup', |
