Repository: matrix-org/synapse

Issue:
Debian package: `homeserver.yaml` is world-readable
**Description: Restrict permissions of homeserver.yaml and other sensitive files.**

By default, it seems that homeserver.yaml has ownership of 644. Given that this file can contain the registration key, turn secret, or other sensitive info, I believe this file should not be readable by all system users.

In addition, the homeserver.signing.key has permissions of 644, also making it readable by all system users.

I would suggest restricting the visibility of this file only to the user who needs to be able to read it (matrix-synapse on Ubuntu).

note: This is the default behaviour I've observed when installing matrix-synapse on Ubuntu 18.04 via the repo at [https://packages.matrix.org/debian/](url).


Discussion:
related to #1528 
This problem is still present 3 years later.

```
deploy@host224:~$ ls -ld /etc/matrix-synapse/
drwxr-xr-x 3 matrix-synapse matrix-synapse 4096 Jan 18 15:42 /etc/matrix-synapse/
deploy@host224:~$ ls -ld /etc/matrix-synapse/homeserver.yaml 
-rw-r--r-- 1 root root 4821 Dec 27 14:38 /etc/matrix-synapse/homeserver.yaml
```

Currently any process on the host can read the secrets contained in the configuration file (database password, registration shared secret, macaroon secret key...

Changing the file mode to something more restrictive (removing world read permissions) prevents synapse from starting.

I think fixing this should be a higher priority. This issue is labeled `Z-Help-Wanted:  We know exactly how to fix this issue, and would be grateful for any contribution`, I could contribute a patch if someone could take the time to detail how to fix this (@richvdh ?)
> Changing the file mode to something more restrictive (removing world read permissions) prevents synapse from starting.

Obviously, you will need to change the ownership as well so that the `matrix-synapse` user can read the file.

Ownership and permissions can both be set via `dpkg-statoverride` in the postinst file; see https://github.com/matrix-org/synapse/blob/develop/debian/matrix-synapse-py3.postinst#L46-L50 for an existing example.  See also https://www.debian.org/doc/debian-policy/ch-files.html#the-use-of-dpkg-statoverride.

By the way, you might consider fixing https://github.com/matrix-org/synapse/issues/2955 while you're in the area.

Proposed fix in https://github.com/matrix-org/synapse/pull/15337 (for `/etc/matrix-synapse/homeserver.yaml` permissions).

This issue also mentions `homeserver.signing.key` which I didn't fix, must be addressed by a separate pull request.

Resolving patch:
diff --git a/synapse/_scripts/generate_signing_key.py b/synapse/_scripts/generate_signing_key.py
--- a/synapse/_scripts/generate_signing_key.py
+++ b/synapse/_scripts/generate_signing_key.py
@@ -13,6 +13,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 import argparse
+import os
 import sys
 
 from signedjson.key import generate_signing_key, write_signing_keys
@@ -26,15 +27,21 @@ def main() -> None:
     parser.add_argument(
         "-o",
         "--output_file",
-        type=argparse.FileType("w"),
-        default=sys.stdout,
+        type=str,
+        default="-",
         help="Where to write the output to",
     )
     args = parser.parse_args()
 
     key_id = "a_" + random_string(4)
     key = (generate_signing_key(key_id),)
-    write_signing_keys(args.output_file, key)
+    if args.output_file == "-":
+        write_signing_keys(sys.stdout, key)
+    else:
+        with open(
+            args.output_file, "w", opener=lambda p, f: os.open(p, f, mode=0o640)
+        ) as signing_key_file:
+            write_signing_keys(signing_key_file, key)
 
 
 if __name__ == "__main__":
diff --git a/synapse/config/key.py b/synapse/config/key.py
--- a/synapse/config/key.py
+++ b/synapse/config/key.py
@@ -263,7 +263,9 @@ def generate_files(self, config: Dict[str, Any], config_dir_path: str) -> None:
 
         if not self.path_exists(signing_key_path):
             print("Generating signing key file %s" % (signing_key_path,))
-            with open(signing_key_path, "w") as signing_key_file:
+            with open(
+                signing_key_path, "w", opener=lambda p, f: os.open(p, f, mode=0o640)
+            ) as signing_key_file:
                 key_id = "a_" + random_string(4)
                 write_signing_keys(signing_key_file, (generate_signing_key(key_id),))
         else:
@@ -274,7 +276,9 @@ def generate_files(self, config: Dict[str, Any], config_dir_path: str) -> None:
                 key = decode_signing_key_base64(
                     NACL_ED25519, key_id, signing_keys.split("\n")[0]
                 )
-                with open(signing_key_path, "w") as signing_key_file:
+                with open(
+                    signing_key_path, "w", opener=lambda p, f: os.open(p, f, mode=0o640)
+                ) as signing_key_file:
                     write_signing_keys(signing_key_file, (key,))
 
 


Test patch: