Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/fix 20 #21

Merged
merged 7 commits into from
Sep 7, 2024
Merged

Feature/fix 20 #21

merged 7 commits into from
Sep 7, 2024

Conversation

mirisbowring
Copy link
Contributor

@mirisbowring mirisbowring commented Jul 1, 2024

Hi,

i debugged this plugin and found 2 errors that lead to #20 .

1. Relative Paths

with open(identifier, "rb") as f:

Since this plugin reads a possible public key from gitconfig, this line resulted in an Error:

[Errno 2] No such file or directory: '~/.ssh/id_ed25519.pub'

To fix this issue I propose to use os.path.expanduser to fix this common mistake:

https://github.com/mirisbowring/gitbark-core/blob/d30c7ed2fcd6c0d172f4f7cd7af6433ef3553398/bark_core/signatures.py#L356

2. False starting index

During the __init__ of the SshKey you are splitting the read public key to extract the type and the key itself. But during the while iteration you start at 1 instead of 0 which results in an infinite loop until python reaches an out of index since i > len(parts) is never catched.

My proposed fix: Start with index 0

https://github.com/mirisbowring/gitbark-core/blob/d30c7ed2fcd6c0d172f4f7cd7af6433ef3553398/bark_core/signatures.py#L325

3. SSH comment interpreted as mail

While testing the bark verify i experienced a behaviour, that ssh signatures (though they are valid) are marked as invalid (without a reason). I did a deep dive into the code and discovered the following problem:

self._emails = parts[0].decode().split(",")

The line above ready the first part of the public key as email which will NEVER work since the first part of the public key is always the keyspec like ssh_ed25519. Therefor, the mail Validation in L342 will always fail:

if email not in self._emails:

To fix this issue, i set the index of the parts list to 2 (remember the format: keyspec_pubkey_comment):

https://github.com/mirisbowring/gitbark-core/blob/433326c1f1d256260047afa821fdafd7b731d312/bark_core/signatures.py#L324

I somehow understand why this check is implemented. Anyway: I strongly recommend to hint this behaviour in the user docs! Since this last part of the public key is specified as comment, there will not always be an email there which will fail every bark verification!

@mirisbowring
Copy link
Contributor Author

@dainnilsson @elibon99 could you please have a look on the proposed bug fixes? We want to introduce this into our development flow but those bugs are currently showstoppers.

@elibon99
Copy link
Collaborator

elibon99 commented Jul 2, 2024

Hi @mirisbowring!

We are happy to hear that you are interested in incorporating GitBark into your development workflow. Thank you for your detailed feedback and proposed changes. Here are my thoughts on your suggestions:

1. Relative Paths

with open(identifier, "rb") as f:

Since this plugin reads a possible public key from gitconfig, this line resulted in an Error:

[Errno 2] No such file or directory: '~/.ssh/id_ed25519.pub'

To fix this issue I propose to use os.path.expanduser to fix this common mistake:

Nice catch! Your suggestion with os.path.expanduser looks good and we are happy to merge this fix.

2. False starting index

During the __init__ of the SshKey you are splitting the read public key to extract the type and the key itself. But during the while iteration you start at 1 instead of 0 which results in an infinite loop until python reaches an out of index since i > len(parts) is never catched.

My proposed fix: Start with index 0

https://github.com/mirisbowring/gitbark-core/blob/d30c7ed2fcd6c0d172f4f7cd7af6433ef3553398/bark_core/signatures.py#L325

3. SSH comment interpreted as mail

While testing the bark verify i experienced a behaviour, that ssh signatures (though they are valid) are marked as invalid (without a reason). I did a deep dive into the code and discovered the following problem:

self._emails = parts[0].decode().split(",")

The line above ready the first part of the public key as email which will NEVER work since the first part of the public key is always the keyspec like ssh_ed25519. Therefor, the mail Validation in L342 will always fail:

if email not in self._emails:

To fix this issue, i set the index of the parts list to 2 (remember the format: keyspec_pubkey_comment):

https://github.com/mirisbowring/gitbark-core/blob/433326c1f1d256260047afa821fdafd7b731d312/bark_core/signatures.py#L324

The reason for why you are experiencing the above issues (2 & 3) is because we require SSH public keys to adhere to the format used in the ALLOWED SIGNERS file. In the context of GitBark, this means that a SSH public key file must contain the following space-separated fields: principals (comma-separated list of authorized email addresses), options (optional), keytype, base64-encoded key. This is why we read the first part of the public key file as the list of authorized emails:

self._emails = parts[0].decode().split(",")

We understand that this requirement is not well-documented, and we appreciate your effort in identifying this problem. GitBark is still a work in progress, and we are continually striving to improve it as time allows.

As you've already discovered, parsing an SSH public key from an identifier given by gitconfig during bark setup may fail due to an incorrect public key file format. Until this is addressed, we recommend manually adding the public key in .bark/pubkeys with the correct format.

@mirisbowring
Copy link
Contributor Author

Ah, that makes sense. In this case, wouldn't it make more sense to parse the gitconfig for the authorized_signers file (if configured)?

@elibon99
Copy link
Collaborator

elibon99 commented Jul 3, 2024

Ah, that makes sense. In this case, wouldn't it make more sense to parse the gitconfig for the authorized_signers file (if configured)?

I understand your point. However, when parsing a public key from gitconfig, our assumption is that the user wants to add just their own public key to the repository. Currently, this does not work for SSH public keys due to the invalid format, so a fix is needed in this area.

The authorized_signers file typically contains multiple public keys, which is why we are not using it in this context. However, enabling users to check in authorized_signers files within the repository (i.e., a file that lists multiple public keys) is a feature we are planning for future updates. Currently, each public key maps to its own file.

@mirisbowring
Copy link
Contributor Author

mirisbowring commented Jul 3, 2024

I think, having pubkey files per user is not a bad idea for the following reasons:

  1. depending on the used crypto algo they cen be relatively large
  2. it would be possible to e.g. protect the keys differently. e.g. a maintainer pub key cannot be removed by any developer while developers can remove other developer keys...

For the initial import of public Keys it would be great if the user can select which public keys from authorized_signers he wants to include :)

I understand your point. However, when parsing a public key from gitconfig, our assumption is that the user wants to add just their own public key to the repository. Currently, this does not work for SSH public keys due to the invalid format, so a fix is needed in this area.

Probably there is a need for an additional function that gets called if the key is read from gitconfig and else the default behaviour steps in?

@elibon99
Copy link
Collaborator

elibon99 commented Jul 4, 2024

Probably there is a need for an additional function that gets called if the key is read from gitconfig and else the default behaviour steps in?

Yes! A simple way to accomplish this would be to parse the keytype and the base64-encoded key from the public key file pointed to by git config user.signingKey (e.g. ~/.ssh/id_ed25519.pub), and use git config user.email as the email address. Then we can use these values to construct a public key file that adheres to the ALLOWED SIGNERS format.

Additionally this commit introduces a mechanism to check if the
configured signing key is an ssh key. If true it creates a tmp pubkey
file that contains the user.email and user.signingKey from .gitconfig to
form a pubkey with a valid format according to authorized_signers
file.
@mirisbowring
Copy link
Contributor Author

Ok, I reverted the initial changes with the index of the email for the public ssh keys.

Additionally i modified the get_pubkey_from_git function to check whether the gpg.format = ssh was set (meaning that ssh key is used for signing). If true, it creates a temporary file with

user.email user.signingKey

After adding the key, this temporary pubkey file gets deleted again.

@mirisbowring
Copy link
Contributor Author

@elibon99 any chance this gets merged soon? 😊

@@ -385,6 +387,17 @@ def get_authorized_pubkeys(

def get_pubkey_from_git() -> Optional[Pubkey]:
identifier = cmd("git", "config", "user.signingKey", check=False)[0]
gpg_format = cmd("git", "config", "gpg.format", check=False)[0]
# check if signingKey is ssh
if gpg_format and gpg_format == "ssh":
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's no need to parse and check gpg_format if identifier is None. I think it would be better to get gpg_format and perform the check here: https://github.com/YubicoLabs/gitbark-core/pull/21/files#diff-398bcc9bee5bed4a56368526a8c80e787bbbc105bb6b064fd6d7339b54413a3fR401.

identifier = os.path.expanduser(identifier) # expand path if relative
with open(identifier, 'r') as file: # read public key
pubkey = file.read()
pubkey_tmp = f"{email} {pubkey}" # join email & pubkey to create correct format
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of creating a temporary file with the correct format, we could return an SshKey object directly: SshKey(f"{email} ".encode() + pubkey.read()).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is an awesome idea! Will try it if i have some free time.

Copy link
Collaborator

@elibon99 elibon99 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for the late response. I had a look at the changes and in general it looks good. I have a few minor thoughts and comments. Please have a look at them

@mirisbowring
Copy link
Contributor Author

Hi @elibon99 finally, i had time to implement the proposed changes!

if gpg_format and gpg_format == "ssh":
email = cmd("git", "config", "user.email", check=False)[0]
identifier = os.path.expanduser(identifier) # expand path if relative
with open(identifier, 'r') as file: # read public key
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Change file reading mode from 'r' to 'rb'.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done 😊

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You will have to remove pubkey.encode(), since pubkey is now already in bytes.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

😄

@elibon99 elibon99 merged commit c5811aa into YubicoLabs:main Sep 7, 2024
1 check passed
@elibon99
Copy link
Collaborator

elibon99 commented Sep 7, 2024

Thanks for your contribution! This has now been merged.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants