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

scripts: script update must be approved #6

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions scripts/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ docker image.

## Updating

Whenever `run_bp.py` is changed, `run_bp.py.sha1` needs to be updated
correspondingly with:
The commit updating the script must be signed whenever `run_bp.py` is changed. Check the [GitHub Help](https://help.github.com/en/articles/signing-commits) to set it up.

sha1sum run_bp.py | awk '{ print $1 }' > run_bp.py.sha1
After committing the file, several approvers(check `_SCRIPT_APPROVER` and `_SCRIPT_APPROVE_THRESHOLD` in the script) then update their `run_bp.py.APPROVER_GITHUB_ID` with the commit hash of the `run_bp.py`. Note that the approver's commit must be signed to be considered a valid approval.
103 changes: 95 additions & 8 deletions scripts/run_bp.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,26 @@
print('Please run `pip3 install ntplib\'')
sys.exit(1)


_SCRIPT_ORG = 'tangerine-network'
_SCRIPT_REPO = 'go-tangerine'
_SCRIPT_BRANCH = 'master'
_SCRIPT_PATH = 'scripts/run_bp.py'
_SCRIPT_SRC = ('https://raw.githubusercontent.com/'
'tangerine-network/go-tangerine/master/scripts/run_bp.py')
'%s/%s/%s/%s' %
(_SCRIPT_ORG,
_SCRIPT_REPO,
_SCRIPT_BRANCH,
_SCRIPT_PATH))
_SCRIPT_APPROVE_PATH_TMPL = _SCRIPT_PATH + '.%s'
_SCRIPT_APPROVE_SRC_TMPL = ('https://raw.githubusercontent.com/'
'%s/%s/%%s/%s' %
(_SCRIPT_ORG,
_SCRIPT_REPO,
_SCRIPT_APPROVE_PATH_TMPL))
_SCRIPT_APPROVER = ['aitjcize', 'JM00oo', 'Spiderpowa']
_SCRIPT_APPROVE_THRESHOLD = int(len(_SCRIPT_APPROVER)/2)

_GITHUB_API = 'https://api.github.com'

_REQUEST_TIMEOUT = 5
_CONTAINER_NAME_BASE = 'tangerine'
Expand Down Expand Up @@ -155,20 +172,87 @@ def check_environment():
'system time')


def github_get_commits(path):
return ('%s/repos/%s/%s/commits?path=%s&sha=%s' %
(_GITHUB_API,
_SCRIPT_ORG,
_SCRIPT_REPO,
path,
_SCRIPT_BRANCH))


def github_get_approved_commit(commit, approver):
with urllib.request.urlopen(
github_get_commits(_SCRIPT_APPROVE_PATH_TMPL % approver),
timeout=_REQUEST_TIMEOUT) as f:
if f.getcode() != 200:
raise RuntimeError('unable to get approver metadata')
for item in json.loads(f.read()):
if not item['commit']['verification']['verified']:
continue
if item['author']['login'] != approver:
continue
with urllib.request.urlopen(
_SCRIPT_APPROVE_SRC_TMPL % (commit, approver),
timeout=_REQUEST_TIMEOUT) as f2:
if f2.getcode() != 200:
raise RuntimeError('unable to get approver file')
if f2.read().decode('utf-8') == commit:
return True
return False


def github_get_approve_status(commit):
approved = 0
for approver in _SCRIPT_APPROVER:
if github_get_approved_commit(commit, approver):
approved += 1
return approved >= _SCRIPT_APPROVE_THRESHOLD


def check_for_update():
"""Check for script update."""
script_path = os.path.abspath(sys.argv[0])
global sha1sum

if sha1sum is None:
with open(script_path, 'r') as f:
sha1sum = hashlib.sha1(f.read().encode('utf-8')).hexdigest()

with urllib.request.urlopen(_SCRIPT_SRC + '.sha1',
with open(script_path, 'rb') as f:
data = f.read()
size = len(data)
sha1sum = hashlib.sha1(
('blob ' + str(size) + "\0" +
data.decode('utf-8')).encode('utf-8')).hexdigest()

found = False
with urllib.request.urlopen(github_get_commits(_SCRIPT_PATH),
timeout=_REQUEST_TIMEOUT) as f:
if f.getcode() != 200:
raise RuntimeError('unable to get upgrade metadata')
update_sha1sum = f.read().strip().decode('utf-8')
for item in json.loads(f.read()):
if not item['commit']['verification']['verified']:
continue
if not github_get_approve_status(item['sha']):
continue
tree_url = item['commit']['tree']['url']
for segment in _SCRIPT_PATH.split('/'):
with urllib.request.urlopen(tree_url,
timeout=_REQUEST_TIMEOUT) as furl:
if f.getcode() != 200:
raise RuntimeError('error finding upgrade metadata')
found_segment = False
for item in json.loads(furl.read())['tree']:
if item['path'] == segment:
tree_url = item['url']
update_sha1sum = item['sha']
found_segment = True
break
if not found_segment:
raise RuntimeError('unable to find upgrade metadata')
found = True
break

if not found:
raise RuntimeError('unable to find a valid upgrade metadata')

if sha1sum != update_sha1sum:
print('Script upgrade found, performing upgrade ...')
Expand All @@ -177,7 +261,10 @@ def check_for_update():

with urllib.request.urlopen(_SCRIPT_SRC, timeout=_REQUEST_TIMEOUT) as f:
script_data = f.read()
new_sha1sum = hashlib.sha1(script_data).hexdigest()
size = len(script_data)
new_sha1sum = hashlib.sha1(
('blob ' + str(size) + "\0" +
script_data.decode('utf-8')).encode('utf-8')).hexdigest()

if new_sha1sum != update_sha1sum:
raise RuntimeError('failed to verify upgrade payload, aborted')
Expand Down
2 changes: 1 addition & 1 deletion scripts/run_bp.py.sha1
Original file line number Diff line number Diff line change
@@ -1 +1 @@
aaa8256dee8030ef876b996c6c4e4e91136ff9a0
854cce9e78d1d74332c3b9a0a41a10e38b72e45e