~edwargix/git.sr.ht

0decd5a1dbbfd4e089650407743c9c028cc7217d — Drew DeVault 8 years ago cb938b7
Implement authorized keys command
11 files changed, 168 insertions(+), 0 deletions(-)

M .gitignore
M Makefile
A git-srht-keys
M gitsrht/blueprints/public.py
R templates/cgit.html => gitsrht/templates/cgit.html
R templates/git.html => gitsrht/templates/git.html
R templates/manage.html => gitsrht/templates/manage.html
R templates/oauth-error.html => gitsrht/templates/oauth-error.html
R templates/user.html => gitsrht/templates/user.html
A setup.py
A static
M .gitignore => .gitignore +1 -0
@@ 13,3 13,4 @@ pip-selfcheck.json
.sass-cache/
overrides/
.pgp
build

M Makefile => Makefile +1 -0
@@ 1,2 1,3 @@
SRHT_PATH?=/usr/lib/python3.6/site-packages/srht
MODULE=gitsrht/
include ${SRHT_PATH}/Makefile

A git-srht-keys => git-srht-keys +132 -0
@@ 0,0 1,132 @@
#!/usr/bin/env python3
# AuthorizedKeysCommand=/usr/bin/git-srht-keys "%u" "%h" "%k"
# AuthorizedKeysUser=root
import sys
import os
import shlex
import requests
from datetime import datetime
from pwd import getpwnam
from grp import getgrnam
from srht.config import cfg, cfgi, load_config
load_config("git")

_log = None
try:
    _log = open("/var/log/git-srht-keys", "a")
except:
    pass

def log(s, *args):
    if isinstance(s, str):
        s = s.format(*args)
    else:
        s = str(s)
    s = "{} {}".format(datetime.now().isoformat(), s)
    if _log:
        _log.write(s + "\n")
    else:
        sys.stderr.write(s + "\n")

def auth_keys_error():
    log("This command should be run by sshd's AuthorizedKeysCommand")
    log('AuthorizedKeysCommand={} "%u" "%h" "%k"\nAuthorizedKeysUser=root',
        os.path.abspath(sys.argv[0]))
    sys.exit(1)

def drop_root():
    if os.getuid() != git_uid or os.getgid() != git_gid:
        log("setuid to {}", git_user)
        os.setgid(git_gid)
        os.setuid(git_uid)

git_user = cfg("git.sr.ht", "git-user").split(':')
git_uid, git_gid = getpwnam(git_user[0]).pw_uid, getgrnam(git_user[-1]).gr_gid
repos = cfg("cgit", "repos")

def auth_keys():
    if len(sys.argv) != 5:
        auth_keys_error()

    user = sys.argv[2]
    uid = getpwnam(user).pw_uid
    homedir = sys.argv[3]
    b64key = sys.argv[4]
    authorized_keys_file = "{}/.ssh/authorized_keys".format(homedir)

    log("user={} home={} b64key={}", user, homedir, b64key)

    if user != git_user[0]:
        log("Falling back to existing authorized keys file")
        if not os.path.exists(authorized_keys_file):
            sys.exit(0)
        with open(authorized_keys_file, "r") as f:
            authorized_keys = f.read()
        print(authorized_keys)
        sys.exit(0)

    drop_root()

    from srht.database import DbSession
    db = DbSession(cfg("sr.ht", "connection-string"))
    from gitsrht.types import User
    db.init()

    r = requests.get("{}/api/ssh-key/{}".format(
        cfg("network", "meta"), b64key))
    if r.status_code != 200:
        log("meta.sr.ht returned 404 for this key")
        sys.exit(0)
    j = r.json()
    username = j["user"]["username"]
    u = User.query.filter(User.username == username).first()
    if not u:
        log("Unknown user {}", username)
    log("Authorized user for login")
    keys = "command=\"{} shell '{}' '{}'\",".format(sys.argv[0], u.id, b64key) + \
        "no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty " + \
        "ssh-rsa {} {}".format(b64key, username)
    print(keys)
    log(keys)
    sys.exit(0)

def shell():
    log("Starting up git.sr.ht shell")
    drop_root()

    _cmd = os.environ.get("SSH_ORIGINAL_COMMAND")
    if not _cmd:
        _cmd = ""
    if len(sys.argv) < 3:
        log("Error: expected 2 arguments from SSH")
    user_id = sys.argv[2]
    ssh_key = sys.argv[3]

    from srht.database import DbSession
    db = DbSession(cfg("sr.ht", "connection-string"))
    from gitsrht.types import User
    db.init()

    user = User.query.filter(User.id == user_id).first()
    if not user:
        log("Unknown user ID {}", user_id)
    log("User: {}", user.username)

    log("Executing {}", _cmd)
    cmd = shlex.split(_cmd)
    valid_commands = ["git-receive-pack", "git-upload-pack", "git-upload-archive"]
    if len(cmd) < 1 or not cmd[0] in valid_commands:
        log("Not permitting unacceptable command")
        print("Hi {}! You've successfully authenticated, ".format(user.username) +
            "but I do not provide an interactive shell. Bye!")
        sys.exit(128)
    os.chdir(repos)
    os.execv("/usr/bin/git-shell", ["/usr/bin/git-shell", "-c", _cmd])

with _log or sys.stdout:
    if len(sys.argv) < 2:
        auth_keys_error()
    if sys.argv[1] == "auth":
        auth_keys()
    if sys.argv[1] == "shell":
        shell()

M gitsrht/blueprints/public.py => gitsrht/blueprints/public.py +3 -0
@@ 37,7 37,10 @@ def user_index(username):
    if search:
        repos = repos.filter(Repository.name.ilike("%" + search + "%"))
    repos = repos.order_by(Repository.updated.desc())
    total_repos = repos.count()
    total_pages = repos.count() // 5 + 1
    if total_repos % 5 == 0:
        total_pages -= 1
    if page:
        try:
            page = int(page) - 1

R templates/cgit.html => gitsrht/templates/cgit.html +0 -0
R templates/git.html => gitsrht/templates/git.html +0 -0
R templates/manage.html => gitsrht/templates/manage.html +0 -0
R templates/oauth-error.html => gitsrht/templates/oauth-error.html +0 -0
R templates/user.html => gitsrht/templates/user.html +0 -0
A setup.py => setup.py +30 -0
@@ 0,0 1,30 @@
#!/usr/bin/env python3
from distutils.core import setup
import subprocess
import glob

subprocess.call(["make"])

setup(
  name = 'gitsrht',
  packages = [
      'gitsrht',
      'gitsrht.types',
      'gitsrht.blueprints',
  ],
  version = subprocess.run(['git', 'describe', '--tags'],
      stdout=subprocess.PIPE).stdout.decode().strip(),
  description = 'git.sr.ht website',
  author = 'Drew DeVault',
  author_email = 'sir@cmpwn.com',
  url = 'https://git.sr.ht/~sircmpwn/git.sr.ht',
  requires = ['srht'],
  license = 'GPL-2.0',
  package_data={
      'gitsrht': [
          'templates/*.html',
          'static/*',
      ]
  },
  scripts = ['git-srht-keys']
)

A static => static +1 -0
@@ 0,0 1,1 @@
gitsrht/static/
\ No newline at end of file