记踩过的一些 Git 坑

Git 大坑

最近在写一个关于github自动发布的东西,被github的各种api折腾的不行TuT
大概记一下,一些遇到的问题
首先是向github添加sshkey:
api接口是
POST /user/keys
input:

1
2
3
"title": "octocat@octomac",
"key": "ssh-rsa AAA..."
}

response

1
2
Status: 201 Created
Location: https://api.github.com/user/keys/1

1
2
3
4
5
6
7
8
"id": 1,
"key": "ssh-rsa AAA...",
"url": "https://api.github.com/user/keys/1",
"title": "octocat@octomac",
"verified": true,
"created_at": "2014-12-10T15:53:42Z",
"read_only": true
}

找了下github3.py这个库里是有这个接口的

1
2
3
4
from github3 import login
g = login(username, password)
key = g.create_key(key_name, public_key)

但是如果这台机器上没有ssh的key
就需要用脚本顺带生成一下
在这里我用的是paramiko这个库
private_key = paramiko.rsakey.RSAKey.generate(2048)
这样会生成一个私钥
public_key = paramiko.RSAKey.from_private_key_file(private_key_file).get_base64()
我们平时cat ~/.ssh/id_rsa.pub看到的公钥是base64过的,但是直接生成的并没有,所以这里需要get_base64()转换一下

现在添加的时候可能会遇到几个问题
1、添加时返回422的错误,并且error message为key is already in use.
这个问题困扰了我很久,因为怎么看帐号上都没有添加上这个key,后来查了下这个错误,发现是github不允许一个key在两个帐号上同时使用。# 这个是因为当你在命令行用git时,是完全通过key来鉴权的,如果允许两个的话就不能标志出你使用的是哪个帐号,github就不知道你现在是谁了。

所以如果写脚本的话,如果会涉及一个key的多次使用,当一次使用完毕后可以把这个key删掉

1
2
key = g.create_key(key_name, public_key)
key.delete()

!!!不要相信github3的doc的example
用他的g.delete_key(key.id)怎么都报错,去看源码才发现!根本没有这个方法
2.添加的key格式错误,会报需要前缀为ssh-rsa, ssh-dss等。
这是因为直接用paramiko创建出的公钥是只含有公钥那段base64的。需要我们手动的把前缀ssh-rsa和后缀的邮箱,即在命令行里执行ssh-keygen是-C参数后面带的注释

1
public_key = "ssh-rsa {0} xxx@example.com\n".format(public_key)

3.公钥添加成功之后依旧permission deny。本来还以为是不可以像2中那样直接自己拼前缀
在命令行里push了下试试就报了下面这样的错

1
2
3
4
5
6
7
8
9
10
11
12
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@ WARNING: UNPROTECTED PRIVATE KEY FILE! @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
Permissions 0644 for '/Users/a1opex/.ssh/id_rsa' are too open.
It is required that your private key files are NOT accessible by others.
This private key will be ignored.
Load key "/Users/a1opex/.ssh/id_rsa": bad permissions
Permission denied (publickey).
fatal: Could not read from remote repository.
Please make sure you have the correct access rights
and the repository exists.

私钥权限过高的warning。解决办法就是

1
chmod 0600 ~/.ssh/id_rsa

最后加一些异常处理之后总得的代码
里面还简单粗暴的用shell解决问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
import os
import paramiko
def create_private_key(file):
pri_key = paramiko.rsakey.RSAKey.generate(2048)
with open(file, 'w') as f:
pri_key.write_private_key(f, None)
os.system("chmod 0600 %s" % file)
return pri_key
def create_public_key(file):
public_key = paramiko.RSAKey.from_private_key_file(file).get_base64()
public_key = "ssh-rsa {0} xxx@example.com\n".format(public_key)
filename = file + '.pub'
with open(filename, 'w') as f:
f.write(public_key)
return str(public_key)
def adding_key(g, path, key_name):
if not (path and key_name): # Equivalent to not path or not name
# print("Cannot create a new key without a path or name")
raise ValueError('Cannot create a new key without a path or name')
pub_path = path + '.pub'
if os.path.isfile(path) and os.path.isfile(pub_path):
with open(pub_path, 'r+') as key_file:
pub_key = key_file.read()
else:
pri = create_private_key(path)
if pri:
pub_key = create_public_key(path)
else:
raise ValueError('Cannot create a new key because create private key failed')
key = g.create_key(key_name, pub_key)
if key:
#print('Key {0} created.'.format(key.title))
return key
else:
# print('Key addition failed.')
raise Exception('Key addition failed.')

python模拟命令行的库可以使用gitapi这个库,虽然已经很久没有维护并且妹有详细的文档啥的
支持这些命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
git init
git branch
git id (git log --pretty=format:%H)
git add
git commit
git status
git log
git checkout
git reset
git merge (fails on conflict)
git push
git pull
git fetch
git clone
git tags
git tag

官方example:

1
2
3
4
5
6
7
>>> import gitapi
>>> repo = gitapi.Repo("test_gitapi") #existing folder
>>> repo.git_init()
>>> repo.git_add("file.txt") #already created but not added file
>>> repo.git_commit("Adding file.txt", user="me <me@example.com>")
>>> str(repo['HEAD'].desc)
'Adding file.txt'

clone和push

1
2
3
4
5
6
7
8
9
# clone
gitapi.git_clone(url, path) # path:本地路径 url:仓库url
# push
repo = gitapi.Repo(repo_name)
repo.git_add(".")
user_info = "{un} <{ue}>".format(un=user_name, ue=user_email)
repo.git_commit("", user=user_info)
repo.git_push(destination="origin", branch="master")

备注下ssh-keygen的参数

-t 指定要创建的密钥类型,如:-t dsa | ecdsa | ed25519 | rsa | rsa1

1
2
3
ssh-keygen -t ecdsa
Generating public/private ecdsa key pair.
Enter file in which to save the key (/Users/a1opex/.ssh/id_rsa):

-b bits 指定密钥长度。对于RSA密钥,最小要求768位,默认是2048位

1
2
3
$ ssh-keygen -b 4096
Generating public/private rsa key pair.
Enter file in which to save the key (/Users/a1opex/.ssh/id_rsa):

-C comment 提供一个注释。生成git密钥的时候都会要求注释中写入邮箱名字。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
$ ssh-keygen -t rsa -b 4096 -C "cnssa1opex@gmail.com"
Generating public/private rsa key pair.
Enter file in which to save the key (/Users/a1opex/.ssh/id_rsa):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /Users/a1opex/.ssh/id_rsa.
Your public key has been saved in /Users/a1opex/.ssh/id_rsa.pub.
The key fingerprint is:
SHA256:gd+1K5Lubsk7SkGozr+u0dsVcKZzZDtoax3N/OAPW9c cnssa1opex@gmail.com
The key's randomart image is:
+---[RSA 4096]----+
| |
| . . |
| . + * . |
| . . X B . . |
| . * S * . |
| o . . * * o . . |
| + . +.=.+ + . E|
| o = o=. * . |
| .o=.o==o. . |
+----[SHA256]-----+

-f filename 指定密钥文件名

-l 显示公钥文件的指纹数据。它也支持 RSA1 的私钥。对于RSA和DSA密钥,将会寻找对应的公钥文件,然后显示其指纹数据。

1
2
$ ssh-keygen -lf ~/.ssh/id_rsa.pub
4096 SHA256:gd+1K5Lubsk7SkGozr+u0dsVcKZzZDtoax3N/OAPW9c cnssa1opex@gmail.com (RSA)

-E 用md5的方式查看指纹数据

1
2
$ ssh-keygen -E md5 -lf id_rsa.pub
4096 MD5:26:e5:ad:69:78:fb:ea:1f:06:4a:ae:99:ec:0a:80:9d cnssa1opex@gmail.com (RSA)

Python/Security<br>程序媛<br><br>小狐狸.