diff mbox series

[v3,1/5] Documentation: define protected configuration

Message ID 575676c760d9a2ce4a59d50e93aa0f45d54620ab.1653685761.git.gitgitgadget@gmail.com (mailing list archive)
State New, archived
Headers show
Series config: introduce discovery.bare and protected config | expand

Commit Message

Glen Choo May 27, 2022, 9:09 p.m. UTC
From: Glen Choo <chooglen@google.com>

For security reasons, some config variables are only trusted when they
are specified in so-called 'protected configuration' [1]. A future
commit will introduce another such config variable, so this is a good
time to standardize the documentation and implementation of 'protected
configuration'.

Define 'protected configuration' as global and system-level config, and
mark `safe.directory` 'Protected config only'. In a future commit,
protected configuration will also include "-c".

The following variables are intentionally not marked 'Protected config
only':

- `uploadpack.packObjectsHook` has the same security concerns as
  `safe.directory`, but due to a different implementation, it also
  respects the "-c" option.

  When protected configuration includes "-c", `upload.packObjectsHook`
  will be marked 'Protected config only'.

- `trace2.*` happens to read the same config as `safe.directory` because
  they share an implementation. However, this is not for security
  reasons; it is because we want to start tracing so early that
  repository-level config and "-c" are not available [2].

  This requirement is unique to `trace2.*`, so it does not makes sense
  for protected configuration to be subject to the same constraints.

[1] For example,
https://lore.kernel.org/git/6af83767-576b-75c4-c778-0284344a8fe7@github.com/
[2] https://lore.kernel.org/git/a0c89d0d-669e-bf56-25d2-cbb09b012e70@jeffhostetler.com/

Signed-off-by: Glen Choo <chooglen@google.com>
---
 Documentation/config.txt           |  6 ++++++
 Documentation/config/safe.txt      | 19 ++++++++-----------
 Documentation/glossary-content.txt | 18 ++++++++++++++++++
 3 files changed, 32 insertions(+), 11 deletions(-)

Comments

Junio C Hamano May 27, 2022, 11:29 p.m. UTC | #1
"Glen Choo via GitGitGadget" <gitgitgadget@gmail.com> writes:

>  safe.directory::
> -	These config entries specify Git-tracked directories that are
> -	considered safe even if they are owned by someone other than the
> -	current user. By default, Git will refuse to even parse a Git
> -	config of a repository owned by someone else, let alone run its
> -	hooks, and this config setting allows users to specify exceptions,
> -	e.g. for intentionally shared repositories (see the `--shared`
> -	option in linkgit:git-init[1]).
> +	'(Protected config only) ' These config entries specify

What's the SP in "only) '" doing?

> diff --git a/Documentation/glossary-content.txt b/Documentation/glossary-content.txt
> index aa2f41f5e70..a669983abd6 100644
> --- a/Documentation/glossary-content.txt
> +++ b/Documentation/glossary-content.txt
> @@ -483,6 +483,24 @@ exclude;;
>  	head ref. If the remote <<def_head,head>> is not an
>  	ancestor to the local head, the push fails.
>  
> +[[def_protected_config]]protected configuration::
> +	Protected configuration is configuration that Git considers more
> +	trustworthy because it is unlikely to be tampered with by an
> +	attacker. For security reasons, some configuration variables are
> +	only respected when they are defined in protected configuration.
> ++
> +Protected configuration includes:
> ++
> +- system-level config, e.g. `/etc/git/config`
> +- global config, e.g. `$XDG_CONFIG_HOME/git/config` and
> +  `$HOME/.gitconfig`
> +Protected configuration excludes:
> ++
> +- repository config, e.g. `$GIT_DIR/config` and
> +  `$GIT_DIR/config.worktree`
> +- the command line option `-c` and its equivalent environment variables

The description is a bit unclear what "protected configuration"
refers.

If it is the scopes (as in "git config --show-scope") Git can trust
more, in other words, a statement like this

    safe.directory is honored only when it comes from a protected
    configuration.

is what you want to make easier to write by introducing a new
phrase, perhaps use the word "scope" for more consistency?  E.g.

    Only safe.directory that is defined in a trusted scope is
    honored.

I dunno.

It would make sense to give a rationale behind the seemingly
arbitrary choice of what is and what is not "protected".  Not
necessarily in the glossary, but in the proposed log message of the
commit that makes the decision.  The rationale must help readers to
be able to answer the following questions.

 - The system level is "protected" because?  Is it because we do not
   even try to protect ourselves from those who can write anywhere
   in /etc/ or other system directories?

 - The per-user config is "protected" because?  Is it because our
   primary interest in "protection" is to protect individual users
   from landmines laid in the filesystem by other users, and those
   who can already write into $HOME are not we try to guard against?

 - The per-repo config is not "protected" (i.e. "trusted"), because?
   If we are not honoring a configuration in the repository, why are
   we working in that repository in the first place?

 - The per invocation config is not "protected" (i.e. "trusted"),
   because?  If we cannot trusting our own command line, what
   prevents an attacker from mucking with our command line to say
   "sudo whatever" using the same attack vector?

Thanks.
Derrick Stolee June 2, 2022, 12:42 p.m. UTC | #2
On 5/27/2022 7:29 PM, Junio C Hamano wrote:
> "Glen Choo via GitGitGadget" <gitgitgadget@gmail.com> writes:
>> +[[def_protected_config]]protected configuration::
>> +	Protected configuration is configuration that Git considers more
>> +	trustworthy because it is unlikely to be tampered with by an
>> +	attacker. For security reasons, some configuration variables are
>> +	only respected when they are defined in protected configuration.
>> ++
>> +Protected configuration includes:
>> ++
>> +- system-level config, e.g. `/etc/git/config`
>> +- global config, e.g. `$XDG_CONFIG_HOME/git/config` and
>> +  `$HOME/.gitconfig`
>> +Protected configuration excludes:
>> ++
>> +- repository config, e.g. `$GIT_DIR/config` and
>> +  `$GIT_DIR/config.worktree`
>> +- the command line option `-c` and its equivalent environment variables
> 
> The description is a bit unclear what "protected configuration"
> refers.
> 
> If it is the scopes (as in "git config --show-scope") Git can trust
> more, in other words, a statement like this
> 
>     safe.directory is honored only when it comes from a protected
>     configuration.
> 
> is what you want to make easier to write by introducing a new
> phrase, perhaps use the word "scope" for more consistency?  E.g.
> 
>     Only safe.directory that is defined in a trusted scope is
>     honored.
> 
> I dunno.
> 
> It would make sense to give a rationale behind the seemingly
> arbitrary choice of what is and what is not "protected".  Not
> necessarily in the glossary, but in the proposed log message of the
> commit that makes the decision.  The rationale must help readers to
> be able to answer the following questions.
> 
>  - The system level is "protected" because?  Is it because we do not
>    even try to protect ourselves from those who can write anywhere
>    in /etc/ or other system directories?
> 
>  - The per-user config is "protected" because?  Is it because our
>    primary interest in "protection" is to protect individual users
>    from landmines laid in the filesystem by other users, and those
>    who can already write into $HOME are not we try to guard against?

I think the answers to these two questions is "yes", so they can
be turned into an affirmative sentence:

	We do not event try to protect ourselves from those who can
	write anywhere...

>  - The per-repo config is not "protected" (i.e. "trusted"), because?
>    If we are not honoring a configuration in the repository, why are
>    we working in that repository in the first place?

This requires an example:

	Some workflows use repositories stored in shared directories,
	which are writable by multiple unprivileged users.
 
>  - The per invocation config is not "protected" (i.e. "trusted"),
>    because?  If we cannot trusting our own command line, what
>    prevents an attacker from mucking with our command line to say
>    "sudo whatever" using the same attack vector?

With this argument, I agree that -c config can be considered
protected. At the very least, it is visible to the user when they
are running a command. This would unify our expectations with
uploadPack.packObjectsHook, too.

Thanks,
-Stolee
Junio C Hamano June 2, 2022, 4:53 p.m. UTC | #3
Derrick Stolee <derrickstolee@github.com> writes:

>> It would make sense to give a rationale behind the seemingly
>> arbitrary choice of what is and what is not "protected".  Not
>> necessarily in the glossary, but in the proposed log message of the
>> commit that makes the decision.  The rationale must help readers to
>> be able to answer the following questions.
>> 
>>  - The system level is "protected" because?  Is it because we do not
>>    even try to protect ourselves from those who can write anywhere
>>    in /etc/ or other system directories?
>> 
>>  - The per-user config is "protected" because?  Is it because our
>>    primary interest in "protection" is to protect individual users
>>    from landmines laid in the filesystem by other users, and those
>>    who can already write into $HOME are not we try to guard against?
>
> I think the answers to these two questions is "yes", so they can
> be turned into an affirmative sentence:
>
> 	We do not event try to protect ourselves from those who can
> 	write anywhere...

s/event/even/.

>
>>  - The per-repo config is not "protected" (i.e. "trusted"), because?
>>    If we are not honoring a configuration in the repository, why are
>>    we working in that repository in the first place?
>
> This requires an example:
>
> 	Some workflows use repositories stored in shared directories,
> 	which are writable by multiple unprivileged users.

Hmph, "... and we do not trust these colleagues"?  It might be true,
but sounds a bit weak rationale, at least to me.  A natural reaction
coming form a devil's advocate naïve me would be "well, then I would
not be directly interacting with such a repository; I'd work in a
clone of it of my own, and pull and push as needed".

Isn't the reason more like "users may go spelunking random places in
the filesystem, with PS1 settings and the like that causes some
"git" command invoked automatically in their current directory, and
we want to protect these users from getting harmed by a random
repository with hostile contents in their configuration and hooks
without even realizing they have wandered into such a repository"?

>>  - The per invocation config is not "protected" (i.e. "trusted"),
>>    because?  If we cannot trusting our own command line, what
>>    prevents an attacker from mucking with our command line to say
>>    "sudo whatever" using the same attack vector?
>
> With this argument, I agree that -c config can be considered
> protected. At the very least, it is visible to the user when they
> are running a command. This would unify our expectations with
> uploadPack.packObjectsHook, too.

Yup, that matches my understanding.

In any case, I'd prefer to see not just the definition but the
reasoning behind the decision that made some "protected" while
leaving others not-"protected" clearly documented to help users.

Thanks.
Glen Choo June 2, 2022, 5:39 p.m. UTC | #4
Junio C Hamano <gitster@pobox.com> writes:

> Derrick Stolee <derrickstolee@github.com> writes:
>>>  - The per-repo config is not "protected" (i.e. "trusted"), because?
>>>    If we are not honoring a configuration in the repository, why are
>>>    we working in that repository in the first place?
>>
>> This requires an example:
>>
>> 	Some workflows use repositories stored in shared directories,
>> 	which are writable by multiple unprivileged users.
>
> Isn't the reason more like "users may go spelunking random places in
> the filesystem, with PS1 settings and the like that causes some
> "git" command invoked automatically in their current directory, and
> we want to protect these users from getting harmed by a random
> repository with hostile contents in their configuration and hooks
> without even realizing they have wandered into such a repository"?

Hm, this is my understanding as well, i.e. `safe.directory` is meant to
protect you from shared repositories that you didn't expect, but it lets
you trust the shared repositories that you need (and there is no
protection once you decide to trust the repo).
Glen Choo June 3, 2022, 3:57 p.m. UTC | #5
Junio C Hamano <gitster@pobox.com> writes:

> "Glen Choo via GitGitGadget" <gitgitgadget@gmail.com> writes:
>
>>  safe.directory::
>> -	These config entries specify Git-tracked directories that are
>> -	considered safe even if they are owned by someone other than the
>> -	current user. By default, Git will refuse to even parse a Git
>> -	config of a repository owned by someone else, let alone run its
>> -	hooks, and this config setting allows users to specify exceptions,
>> -	e.g. for intentionally shared repositories (see the `--shared`
>> -	option in linkgit:git-init[1]).
>> +	'(Protected config only) ' These config entries specify
>
> What's the SP in "only) '" doing?

Silly typo. Thanks for the catch :)

>> diff --git a/Documentation/glossary-content.txt b/Documentation/glossary-content.txt
>> index aa2f41f5e70..a669983abd6 100644
>> --- a/Documentation/glossary-content.txt
>> +++ b/Documentation/glossary-content.txt
>> @@ -483,6 +483,24 @@ exclude;;
>>  	head ref. If the remote <<def_head,head>> is not an
>>  	ancestor to the local head, the push fails.
>>  
>> +[[def_protected_config]]protected configuration::
>> +	Protected configuration is configuration that Git considers more
>> +	trustworthy because it is unlikely to be tampered with by an
>> +	attacker. For security reasons, some configuration variables are
>> +	only respected when they are defined in protected configuration.
>> ++
>> +Protected configuration includes:
>> ++
>> +- system-level config, e.g. `/etc/git/config`
>> +- global config, e.g. `$XDG_CONFIG_HOME/git/config` and
>> +  `$HOME/.gitconfig`
>> +Protected configuration excludes:
>> ++
>> +- repository config, e.g. `$GIT_DIR/config` and
>> +  `$GIT_DIR/config.worktree`
>> +- the command line option `-c` and its equivalent environment variables
>
> The description is a bit unclear what "protected configuration"
> refers.
>
> If it is the scopes (as in "git config --show-scope") Git can trust
> more, in other words, a statement like this
>
>     safe.directory is honored only when it comes from a protected
>     configuration.
>
> is what you want to make easier to write by introducing a new
> phrase, perhaps use the word "scope" for more consistency?  E.g.
>
>     Only safe.directory that is defined in a trusted scope is
>     honored.

Good point. I think using scope would be a lot clearer, and maybe I
will consider s/protected configuration/protected scope. I'm hesitant to
call the scope "trusted", because I don't want to insinuate that
repository config is "untrusted" since we _do_ trust it in most cases.

I don't think Documentation/git-config.txt has adequately defined what a
'scope' is though, even though scopes have been with us since 9acc591111
(config: add a notion of "scope", 2016-05-18). The best I could find is
"--show-scope", introduced in 145d59f482 (config: add '--show-scope' to
print the scope of a config value, 2020-02-10), which mentions scopes
but doesn't link the idea back to the specific files or CLI options
("--system", "--global", etc).

So I'll see if I can improve the docs around scopes since that will help
the language in this patch.
diff mbox series

Patch

diff --git a/Documentation/config.txt b/Documentation/config.txt
index e284b042f22..07832de1a6c 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -369,6 +369,12 @@  inventing new variables for use in your own tool, make sure their
 names do not conflict with those that are used by Git itself and
 other popular tools, and describe them in your documentation.
 
+Variables marked with '(Protected config only)' are only respected when
+they are specified in protected configuration. This includes global and
+system-level config, and excludes repository config, the command line
+option `-c`, and environment variables. For more details, see the
+'protected configuration' entry in linkgit:gitglossary[7].
+
 include::config/advice.txt[]
 
 include::config/core.txt[]
diff --git a/Documentation/config/safe.txt b/Documentation/config/safe.txt
index ae0e2e3bdb4..c1caec460e8 100644
--- a/Documentation/config/safe.txt
+++ b/Documentation/config/safe.txt
@@ -1,21 +1,18 @@ 
 safe.directory::
-	These config entries specify Git-tracked directories that are
-	considered safe even if they are owned by someone other than the
-	current user. By default, Git will refuse to even parse a Git
-	config of a repository owned by someone else, let alone run its
-	hooks, and this config setting allows users to specify exceptions,
-	e.g. for intentionally shared repositories (see the `--shared`
-	option in linkgit:git-init[1]).
+	'(Protected config only) ' These config entries specify
+	Git-tracked directories that are considered safe even if they
+	are owned by someone other than the current user. By default,
+	Git will refuse to even parse a Git config of a repository owned
+	by someone else, let alone run its hooks, and this config
+	setting allows users to specify exceptions, e.g. for
+	intentionally shared repositories (see the `--shared` option in
+	linkgit:git-init[1]).
 +
 This is a multi-valued setting, i.e. you can add more than one directory
 via `git config --add`. To reset the list of safe directories (e.g. to
 override any such directories specified in the system config), add a
 `safe.directory` entry with an empty value.
 +
-This config setting is only respected when specified in a system or global
-config, not when it is specified in a repository config, via the command
-line option `-c safe.directory=<path>`, or in environment variables.
-+
 The value of this setting is interpolated, i.e. `~/<path>` expands to a
 path relative to the home directory and `%(prefix)/<path>` expands to a
 path relative to Git's (runtime) prefix.
diff --git a/Documentation/glossary-content.txt b/Documentation/glossary-content.txt
index aa2f41f5e70..a669983abd6 100644
--- a/Documentation/glossary-content.txt
+++ b/Documentation/glossary-content.txt
@@ -483,6 +483,24 @@  exclude;;
 	head ref. If the remote <<def_head,head>> is not an
 	ancestor to the local head, the push fails.
 
+[[def_protected_config]]protected configuration::
+	Protected configuration is configuration that Git considers more
+	trustworthy because it is unlikely to be tampered with by an
+	attacker. For security reasons, some configuration variables are
+	only respected when they are defined in protected configuration.
++
+Protected configuration includes:
++
+- system-level config, e.g. `/etc/git/config`
+- global config, e.g. `$XDG_CONFIG_HOME/git/config` and
+  `$HOME/.gitconfig`
++
+Protected configuration excludes:
++
+- repository config, e.g. `$GIT_DIR/config` and
+  `$GIT_DIR/config.worktree`
+- the command line option `-c` and its equivalent environment variables
+
 [[def_reachable]]reachable::
 	All of the ancestors of a given <<def_commit,commit>> are said to be
 	"reachable" from that commit. More