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

Add support for ssh-principals #19

Open
wants to merge 94 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
94 commits
Select commit Hold shift + click to select a range
aca25db
Python 3
Dec 11, 2018
35a9a8a
Python3
Dec 16, 2018
a5c8c20
Python3
mgukov Dec 16, 2018
a6c329e
added ssh-principals
Jul 31, 2019
72b551f
if only a username is given on init, it is used as principal. no
Jul 31, 2019
0b35036
fixed missing colon in if statement
Jul 31, 2019
5264a15
pubkey handling
Jul 31, 2019
954588c
debug
Jul 31, 2019
e9fd563
debug
Jul 31, 2019
e49279d
filehandle for logger added
Jul 31, 2019
00e9d7d
´filename in quotes
Jul 31, 2019
af521f6
loglevel debug added
Jul 31, 2019
078686c
DEBUG -> "DEBUG"
Jul 31, 2019
d1e27e5
debug
Jul 31, 2019
a3a565e
ssh -> ssh_principals
Jul 31, 2019
d98c6cc
logging added
Jul 31, 2019
4dcd1f3
Debug added
Jul 31, 2019
5fe6c8b
logger->logging
Jul 31, 2019
ac1b528
bool
Jul 31, 2019
161308d
check auf leerzeichen im pubkey
Jul 31, 2019
106522c
debug added
Jul 31, 2019
b3f7f15
logging debug
Jul 31, 2019
c82e894
None logging fixed
Jul 31, 2019
7b97347
principals in keydir/principals
Jul 31, 2019
bd57935
First step AuthorizedPrincipalCommand added
Aug 1, 2019
2b9038c
typo fixed
Aug 1, 2019
60cf43a
typo fixed
Aug 1, 2019
7d993c0
debug
Aug 1, 2019
9b3b7ae
debug
Aug 1, 2019
b88948b
debug
Aug 1, 2019
0d63113
debug
Aug 1, 2019
ba197a1
print line
Aug 1, 2019
efdab87
loop removed
Aug 1, 2019
e2302c2
log removed
Aug 1, 2019
544a26c
user->sshUser
Aug 1, 2019
3d47997
sshUser mit und ohne @
Aug 1, 2019
de05ba9
loop fixed
Aug 1, 2019
7438aed
added logfile
Aug 1, 2019
1fc6d57
log to logifile
Aug 1, 2019
5796ba4
logfile in ~
Aug 1, 2019
6487465
logging entfernt
Aug 1, 2019
3442e2e
logfile in home fixed
Aug 1, 2019
46278d6
logfile
Aug 1, 2019
4497a2b
getAllowedSSHPrincipals from config
Aug 1, 2019
9d50d26
fixed
Aug 1, 2019
42c5352
util fixed
Aug 1, 2019
490c089
missing code added
Aug 1, 2019
ca4479c
Where comes GIT_DIR from?
Aug 1, 2019
e6691d2
git_dir removed
Aug 1, 2019
a96ad0d
git_dir check removed
Aug 1, 2019
648319b
path durch principals ersetzt
Aug 1, 2019
3f9ba85
long username removed
Aug 2, 2019
acc17bc
Use of principals added to README
Aug 2, 2019
6f5727f
formatierung changed
Aug 2, 2019
b8fee87
contact added
Aug 2, 2019
fb9da76
only key ID from ssh-certificate is used
Aug 2, 2019
2a528be
readme static principal-files added
Aug 2, 2019
de80f5c
users in static principal-files
Aug 2, 2019
96c6226
dependency added
Aug 2, 2019
cc9f200
Readme ergänzt
Aug 2, 2019
a0e4459
static principal-files removed, also from README
Aug 2, 2019
bad18e8
Formatting h3 tryout
Aug 2, 2019
be752d3
Formatting fixed (h2 instead of h3)
Aug 2, 2019
fb0fdf2
Initialisation with principals added
Aug 2, 2019
732f818
principals added to example.conf
Aug 2, 2019
87ece5e
principals added
Aug 2, 2019
4d1775f
debuglog
Aug 3, 2019
55b54ae
return added
Aug 3, 2019
64b3391
a line for each principal
Aug 3, 2019
458e7e2
variable renamed
Aug 3, 2019
174de20
debugging
Aug 3, 2019
6c2c4ec
list as input for loop
Aug 3, 2019
26e6fc4
split output
Aug 3, 2019
1a02e07
debug
Aug 3, 2019
34e3738
log entfernt
Aug 3, 2019
e15bad3
only build principal with username out of user@host
Aug 4, 2019
2c7b1c6
print removed
Aug 4, 2019
8fbf51b
yield changed to print
Aug 4, 2019
2039e7f
Add sshd_config snippet for users not git
Aug 28, 2019
9defb27
fix logging if GIT_DIR is none
Sep 5, 2019
98be92f
debug
Sep 5, 2019
ea02faf
fixed logging when GIT_DIR is none
Sep 5, 2019
2cb9ed3
remove debug-log
Sep 5, 2019
0fe0eef
debug added
Sep 5, 2019
ebaf7a7
improve debug
Sep 5, 2019
89921e7
fix improved logging
Sep 5, 2019
b005b18
Merge branch 'principals-with-host'
Oct 29, 2019
74adca7
Merge remote-tracking branch 'mgukov/master'
Oct 2, 2021
1504a33
Python 3
Oct 2, 2021
a620534
fix _levelNames error
Oct 2, 2021
daadb52
change print for python3
Oct 31, 2021
f839d08
change description of repositories
Oct 31, 2021
0934212
change lineend for authorized_keys
Oct 31, 2021
4bb3c5f
change logging format for python 3
Oct 31, 2021
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
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,5 @@
/apidocs
/gitosis/test/tmp
/.coverage
.idea
venv
89 changes: 88 additions & 1 deletion README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,17 @@ more information.

You can get ``gitosis`` via ``git`` by saying::

git clone https://github.com/tv42/gitosis.git
This repositories are from [email protected], support python3 and ssh-certificates
git clone [email protected]:xundeenergie/gitosis.git (fetch)
git clone [email protected]:xundeenergie/gitosis.git (fetch)
git clone [email protected]:public/gitosis.git (fetch)

This repository translates gitosis to python3, but not fully.
git clone [email protected]:mgukov/gitosis.git (push)

Original repository seems unmaintained
git clone [email protected]:tv42/gitosis.git (fetch)


And install it via::

Expand Down Expand Up @@ -75,6 +85,12 @@ it to running ``gitosis-serve``. Run::
sudo -H -u git gitosis-init <FILENAME.pub
# (or just copy-paste the public key when prompted)

If you want to use ssh-certificates with principals, you need a file with
your admin-user in it. Name it adminuser.txt, only one line with your admins
username in it and run::

sudo -H -u git gitosis-init <adminuser.txt

then just ``git clone git@SERVER:gitosis-admin.git``, and you get a
repository with SSH keys as ``keys/USER.pub`` and a ``gitosis.conf``
where you can configure who has access to what.
Expand Down Expand Up @@ -190,12 +206,83 @@ Note that this short snippet is not a substitute for reading and
understanding the relevant documentation.


Using ssh-certificates and principals
=====================================

``ssh certificates`` are a new feature of openssh, where you setup your own ssh-CA
and you sign all you host- and user-pubkeys.

If you want to use certificates ans principals, please visit THIS_ and THIS_ website.
To find out more about the AuthorizedPrincipalCommand in sshd_config, please consult GITLABS_
documentation for it.

.. _THIS: https://ef.gy/hardening-ssh
.. _THIS: https://framkant.org/2017/07/scalable-access-control-using-openssh-certificates/
.. _GITLABS: https://docs.gitlab.com/ee/administration/operations/ssh_certificates.html

To use principals and ssh-certificates with this fork of gitosis, please add this snippet to your sshd_config on your git-server::

Match User git
AuthorizedPrincipalsCommandUser git
AuthorizedPrincipalsCommand /usr/local/bin/gitosis-authorized-principals %i

And for all users except git, use only principal-files::

Match User !git,*
AuthorizedPrincipalsFile /etc/ssh/userprincipals/%u


This will run the command as user "git", which will you have installed, when you serve your gitrepos with gitosis.
%i is the key-identity of your certificate, which will you give on your sign-process to the user-certificate.

Then you need an additional line in your gitosis.conf in the [gitosis]-section::

[gitosis]
...
allowedPrincipals = git gitosis-admin

In the members-line of your gitosis.conf, you have to write down the key-identity (which is passed as %i in you sshd_config). If you are not sure,
what the identity is, try::

ssh-keygen -L -f ~/.ssh/id_rsa-cert.pub

/home/myusername/.ssh/id_rsa-cert.pub:
Type: [email protected] user certificate
Public key: RSA-CERT SHA256:cjLH4l45G32zOaJBjv8Udnr7bkwHRNB3nAz0a6SCOl0
Signing CA: ED25519 SHA256:9bMENs+blen§naslr§BJEN421I5ckbu4mvpnktiHdUs (using ssh-ed25519)
Key ID: "myusername"
Serial: 4
Valid: from 2019-08-02T02:29:00 to 2020-08-01T02:30:20
Principals:
myusername
principal2
git
gitosis-admin
Critical Options: (none)
Extensions:
permit-X11-forwarding
permit-agent-forwarding
permit-port-forwarding
permit-pty
permit-user-rc

from your principals in the key, only git and gitosis-admin are allowed. You must have at least one of this allowed principals in your key, to get access to your gitosis-served repos.
Access is only given, if you have one of the allowed principals in your certificate, and your key ID is listed as member in the repo

Parallel use of principals/certificates an pubkeys
--------------------------------------------------

It is possible, to use pubkeys in parallel to these principals from certificates. Just as described above. If you have a user, which has no certificate from your ssh-CA, just add his
public-sshkey in the keydir. (not tested now)


Contact
=======

You can email the author at ``[email protected]``, or hop on
``irc.freenode.net`` channel ``#git`` and hope for the best.

For ssh-certificates and principals, please contact [email protected]

There will be more, keep an eye on http://eagain.net/ and/or the git
mailing list.
4 changes: 2 additions & 2 deletions TODO.rst
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,9 @@

- can't trust "~"::

[0 tv@musti ~]$ sudo python -c 'import os; print os.path.expanduser("~")'
[0 tv@musti ~]$ sudo python -c 'import os; print(os.path.expanduser("~"))'
/home/tv
[0 tv@musti ~]$ sudo -H python -c 'import os; print os.path.expanduser("~")'
[0 tv@musti ~]$ sudo -H python -c 'import os; print(os.path.expanduser("~"))'
/root

- command line options
Expand Down
6 changes: 6 additions & 0 deletions example.conf
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,12 @@ daemon = no
## Logging level, one of DEBUG, INFO, WARNING, ERROR, CRITICAL
loglevel = DEBUG

## If you use ssh-certificates with principals, you need this option
## If commented, allowedPrincipals defaults to "git". At least, your certificates of the users
## which want to use this repos, must have at least "git" as principal in their
## certificates
allowedPrincipals = git

[group quux]
members = jdoe wsmith @anothergroup
writable = foo bar baz/thud
Expand Down
2 changes: 1 addition & 1 deletion gitosis/access.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import os, logging
from ConfigParser import NoSectionError, NoOptionError
from configparser import NoSectionError, NoOptionError

from gitosis import group

Expand Down
18 changes: 9 additions & 9 deletions gitosis/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import logging
import optparse
import errno
import ConfigParser
import configparser

log = logging.getLogger('gitosis.app')

Expand Down Expand Up @@ -31,14 +31,14 @@ def main(self):
cfg = self.create_config(options)
try:
self.read_config(options, cfg)
except CannotReadConfigError, e:
except CannotReadConfigError as e:
log.error(str(e))
sys.exit(1)
self.setup_logging(cfg)
self.handle_args(parser, cfg, options, args)

def setup_basic_logging(self):
logging.basicConfig()
logging.basicConfig(filename='/home/git/gitosis.log')

def create_parser(self):
parser = optparse.OptionParser()
Expand All @@ -53,13 +53,13 @@ def create_parser(self):
return parser

def create_config(self, options):
cfg = ConfigParser.RawConfigParser()
cfg = configparser.RawConfigParser()
return cfg

def read_config(self, options, cfg):
try:
conffile = file(options.config)
except (IOError, OSError), e:
conffile = open(options.config)
except (IOError, OSError) as e:
if e.errno == errno.ENOENT:
# special case this because gitosis-init wants to
# ignore this particular error case
Expand All @@ -74,12 +74,12 @@ def read_config(self, options, cfg):
def setup_logging(self, cfg):
try:
loglevel = cfg.get('gitosis', 'loglevel')
except (ConfigParser.NoSectionError,
ConfigParser.NoOptionError):
except (configparser.NoSectionError,
configparser.NoOptionError):
pass
else:
try:
symbolic = logging._levelNames[loglevel]
symbolic = logging._nameToLevel[loglevel]
except KeyError:
log.warning(
'Ignored invalid loglevel configuration: %r',
Expand Down
6 changes: 3 additions & 3 deletions gitosis/gitdaemon.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import logging
import os

from ConfigParser import NoSectionError, NoOptionError
from configparser import NoSectionError, NoOptionError

log = logging.getLogger('gitosis.gitdaemon')

Expand All @@ -14,13 +14,13 @@ def export_ok_path(repopath):

def allow_export(repopath):
p = export_ok_path(repopath)
file(p, 'a').close()
open(p, 'a').close()

def deny_export(repopath):
p = export_ok_path(repopath)
try:
os.unlink(p)
except OSError, e:
except OSError as e:
if e.errno == errno.ENOENT:
pass
else:
Expand Down
14 changes: 8 additions & 6 deletions gitosis/gitweb.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@

import os, urllib, logging

from ConfigParser import NoSectionError, NoOptionError
from configparser import NoSectionError, NoOptionError

from gitosis import util

Expand Down Expand Up @@ -91,8 +91,9 @@ def generate_project_list_fp(config, fp):
else:
response.append(owner)

line = ' '.join([urllib.quote_plus(s) for s in response])
print >>fp, line
line = ' '.join([urllib.parse.quote_plus(s) for s in response])
#print >>fp, line
print(line, end="", file=fp)

def generate_project_list(config, path):
"""
Expand All @@ -106,7 +107,7 @@ def generate_project_list(config, path):
"""
tmp = '%s.%d.tmp' % (path, os.getpid())

f = file(tmp, 'w')
f = open(tmp, 'w')
try:
generate_project_list_fp(config=config, fp=f)
finally:
Expand Down Expand Up @@ -157,9 +158,10 @@ def set_descriptions(config):
'description',
)
tmp = '%s.%d.tmp' % (path, os.getpid())
f = file(tmp, 'w')
f = open(tmp, 'w')
try:
print >>f, description
#print >>f, description
print(description, end="", file=f)
finally:
f.close()
os.rename(tmp, path)
2 changes: 1 addition & 1 deletion gitosis/group.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import logging
from ConfigParser import NoSectionError, NoOptionError
from configparser import NoSectionError, NoOptionError

def _getMembership(config, user, seen):
log = logging.getLogger('gitosis.group.getMembership')
Expand Down
40 changes: 30 additions & 10 deletions gitosis/init.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,11 @@
import logging
import os
import sys
import re

from pkg_resources import resource_filename
from cStringIO import StringIO
from ConfigParser import RawConfigParser
from io import StringIO
from configparser import RawConfigParser

from gitosis import repository
from gitosis import run_hook
Expand All @@ -32,19 +33,32 @@ def __str__(self):
return '%s: %s' % (self.__doc__, ': '.join(self.args))

def ssh_extract_user(pubkey):
_, user = pubkey.rsplit(None, 1)
if not bool(re.search(r"\s", pubkey)):
_, user = pubkey.rsplit(None, 1)
else:
user = pubkey.strip()
if ssh.isSafeUsername(user):
return user
else:
raise InsecureSSHKeyUsername(repr(user))

def initial_commit(git_dir, cfg, pubkey, user):
log.debug('create initial commit')
log.info('User: ' + user)
if pubkey is None:
keyfile = 'keydir/principals'
content = user
else:
keyfile = 'keydir/%s.pub' % user
content = pubkey
log.debug('keyfile' + keyfile)
log.debug('content' + content)
repository.fast_import(
git_dir=git_dir,
commit_msg='Automatic creation of gitosis repository.',
committer='Gitosis Admin <%s>' % user,
files=[
('keydir/%s.pub' % user, pubkey),
(keyfile, content),
('gitosis.conf', cfg),
],
)
Expand All @@ -54,7 +68,7 @@ def symlink_config(git_dir):
tmp = '%s.%d.tmp' % (dst, os.getpid())
try:
os.unlink(tmp)
except OSError, e:
except OSError as e:
if e.errno == errno.ENOENT:
pass
else:
Expand All @@ -80,15 +94,18 @@ def init_admin_repository(

# can't rely on setuptools and all kinds of distro packaging to
# have kept our templates executable, it seems
os.chmod(os.path.join(git_dir, 'hooks', 'post-update'), 0755)
os.chmod(os.path.join(git_dir, 'hooks', 'post-update'), 0o755)

if not repository.has_initial_commit(git_dir):
log.info('Making initial commit...')
# ConfigParser does not guarantee order, so jump through hoops
# to make sure [gitosis] is first
cfg_file = StringIO()
print >>cfg_file, '[gitosis]'
print >>cfg_file
print('[gitosis]', file=cfg_file)
#print('', end="", file=cfg_file)

#print >>cfg_file, '[gitosis]'
#print >>cfg_file
cfg = RawConfigParser()
cfg.add_section('group gitosis-admin')
cfg.set('group gitosis-admin', 'members', user)
Expand Down Expand Up @@ -119,11 +136,14 @@ def read_config(self, *a, **kw):
def handle_args(self, parser, cfg, options, args):
super(Main, self).handle_args(parser, cfg, options, args)

os.umask(0022)
os.umask(0o022)

log.info('Reading SSH public key...')
pubkey = read_ssh_pubkey()
user = ssh_extract_user(pubkey)
if not " " in pubkey:
pubkey = None
log.debug("pubkey: %s", pubkey)
if user is None:
log.error('Cannot parse user from SSH public key.')
sys.exit(1)
Expand All @@ -141,7 +161,7 @@ def handle_args(self, parser, cfg, options, args):
user=user,
)
log.info('Running post-update hook...')
util.mkdir(os.path.expanduser('~/.ssh'), 0700)
util.mkdir(os.path.expanduser('~/.ssh'), 0o700)
run_hook.post_update(cfg=cfg, git_dir=admin_repository)
log.info('Symlinking ~/.gitosis.conf to repository...')
symlink_config(git_dir=admin_repository)
Expand Down
Loading