Merge pull request #86319 from flokli/google-oslogin

nixos/google-oslogin: bump package, make tests more readable
This commit is contained in:
Florian Klink 2020-04-29 16:47:29 +02:00 committed by GitHub
commit c2c30d926c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 86 additions and 44 deletions

View file

@ -22,6 +22,8 @@ in {
client = { ... }: {};
};
testScript = ''
MOCKUSER = "mockuser_nixos_org"
MOCKADMIN = "mockadmin_nixos_org"
start_all()
server.wait_for_unit("mock-google-metadata.service")
@ -29,10 +31,10 @@ in {
# mockserver should return a non-expired ssh key for both mockuser and mockadmin
server.succeed(
'${pkgs.google-compute-engine-oslogin}/bin/google_authorized_keys mockuser | grep -q "${snakeOilPublicKey}"'
f'${pkgs.google-compute-engine-oslogin}/bin/google_authorized_keys {MOCKUSER} | grep -q "${snakeOilPublicKey}"'
)
server.succeed(
'${pkgs.google-compute-engine-oslogin}/bin/google_authorized_keys mockadmin | grep -q "${snakeOilPublicKey}"'
f'${pkgs.google-compute-engine-oslogin}/bin/google_authorized_keys {MOCKADMIN} | grep -q "${snakeOilPublicKey}"'
)
# install snakeoil ssh key on the client, and provision .ssh/config file
@ -50,20 +52,22 @@ in {
client.fail("ssh ghost@server 'true'")
# we should be able to connect as mockuser
client.succeed("ssh mockuser@server 'true'")
client.succeed(f"ssh {MOCKUSER}@server 'true'")
# but we shouldn't be able to sudo
client.fail(
"ssh mockuser@server '/run/wrappers/bin/sudo /run/current-system/sw/bin/id' | grep -q 'root'"
f"ssh {MOCKUSER}@server '/run/wrappers/bin/sudo /run/current-system/sw/bin/id' | grep -q 'root'"
)
# we should also be able to log in as mockadmin
client.succeed("ssh mockadmin@server 'true'")
client.succeed(f"ssh {MOCKADMIN}@server 'true'")
# pam_oslogin_admin.so should now have generated a sudoers file
server.succeed("find /run/google-sudoers.d | grep -q '/run/google-sudoers.d/mockadmin'")
server.succeed(
f"find /run/google-sudoers.d | grep -q '/run/google-sudoers.d/{MOCKADMIN}'"
)
# and we should be able to sudo
client.succeed(
"ssh mockadmin@server '/run/wrappers/bin/sudo /run/current-system/sw/bin/id' | grep -q 'root'"
f"ssh {MOCKADMIN}@server '/run/wrappers/bin/sudo /run/current-system/sw/bin/id' | grep -q 'root'"
)
'';
})

View file

@ -7,24 +7,29 @@ import hashlib
import base64
from http.server import BaseHTTPRequestHandler, HTTPServer
from urllib.parse import urlparse, parse_qs
from typing import Dict
SNAKEOIL_PUBLIC_KEY = os.environ['SNAKEOIL_PUBLIC_KEY']
MOCKUSER="mockuser_nixos_org"
MOCKADMIN="mockadmin_nixos_org"
def w(msg):
def w(msg: bytes):
sys.stderr.write(f"{msg}\n")
sys.stderr.flush()
def gen_fingerprint(pubkey):
def gen_fingerprint(pubkey: str):
decoded_key = base64.b64decode(pubkey.encode("ascii").split()[1])
return hashlib.sha256(decoded_key).hexdigest()
def gen_email(username):
def gen_email(username: str):
"""username seems to be a 21 characters long number string, so mimic that in a reproducible way"""
return str(int(hashlib.sha256(username.encode()).hexdigest(), 16))[0:21]
def gen_mockuser(username: str, uid: str, gid: str, home_directory: str, snakeoil_pubkey: str) -> Dict:
snakeoil_pubkey_fingerprint = gen_fingerprint(snakeoil_pubkey)
# seems to be a 21 characters long numberstring, so mimic that in a reproducible way
@ -56,7 +61,8 @@ def gen_mockuser(username: str, uid: str, gid: str, home_directory: str, snakeoi
class ReqHandler(BaseHTTPRequestHandler):
def _send_json_ok(self, data):
def _send_json_ok(self, data: dict):
self.send_response(200)
self.send_header('Content-type', 'application/json')
self.end_headers()
@ -64,29 +70,62 @@ class ReqHandler(BaseHTTPRequestHandler):
w(out)
self.wfile.write(out)
def _send_json_success(self, success=True):
self.send_response(200)
self.send_header('Content-type', 'application/json')
self.end_headers()
out = json.dumps({"success": success}).encode()
w(out)
self.wfile.write(out)
def _send_404(self):
self.send_response(404)
self.end_headers()
def do_GET(self):
p = str(self.path)
# mockuser and mockadmin are allowed to login, both use the same snakeoil public key
if p == '/computeMetadata/v1/oslogin/users?username=mockuser' \
or p == '/computeMetadata/v1/oslogin/users?uid=1009719690':
self._send_json_ok(gen_mockuser(username='mockuser', uid='1009719690', gid='1009719690',
home_directory='/home/mockuser', snakeoil_pubkey=SNAKEOIL_PUBLIC_KEY))
elif p == '/computeMetadata/v1/oslogin/users?username=mockadmin' \
or p == '/computeMetadata/v1/oslogin/users?uid=1009719691':
self._send_json_ok(gen_mockuser(username='mockadmin', uid='1009719691', gid='1009719691',
home_directory='/home/mockadmin', snakeoil_pubkey=SNAKEOIL_PUBLIC_KEY))
pu = urlparse(p)
params = parse_qs(pu.query)
# mockuser is allowed to login
elif p == f"/computeMetadata/v1/oslogin/authorize?email={gen_email('mockuser')}&policy=login":
self._send_json_ok({'success': True})
# users endpoint
if pu.path == "/computeMetadata/v1/oslogin/users":
# mockuser and mockadmin are allowed to login, both use the same snakeoil public key
if params.get('username') == [MOCKUSER] or params.get('uid') == ["1009719690"]:
username = MOCKUSER
uid = "1009719690"
elif params.get('username') == [MOCKADMIN] or params.get('uid') == ["1009719691"]:
username = MOCKADMIN
uid = "1009719691"
else:
self._send_404()
return
# mockadmin may also become root
elif p == f"/computeMetadata/v1/oslogin/authorize?email={gen_email('mockadmin')}&policy=login" or p == f"/computeMetadata/v1/oslogin/authorize?email={gen_email('mockadmin')}&policy=adminLogin":
self._send_json_ok({'success': True})
self._send_json_ok(gen_mockuser(username=username, uid=uid, gid=uid, home_directory=f"/home/{username}", snakeoil_pubkey=SNAKEOIL_PUBLIC_KEY))
return
# authorize endpoint
elif pu.path == "/computeMetadata/v1/oslogin/authorize":
# is user allowed to login?
if params.get("policy") == ["login"]:
# mockuser and mockadmin are allowed to login
if params.get('email') == [gen_email(MOCKUSER)] or params.get('email') == [gen_email(MOCKADMIN)]:
self._send_json_success()
return
self._send_json_success(False)
return
# is user allowed to become root?
elif params.get("policy") == ["adminLogin"]:
# only mockadmin is allowed to become admin
self._send_json_success((params['email'] == [gen_email(MOCKADMIN)]))
return
# send 404 for other policies
else:
self._send_404()
return
else:
sys.stderr.write(f"Unhandled path: {p}\n")
sys.stderr.flush()
self.send_response(501)
self.send_response(404)
self.end_headers()
self.wfile.write(b'')

View file

@ -1,4 +1,5 @@
{ stdenv
, lib
, fetchFromGitHub
, curl
, json_c
@ -8,22 +9,20 @@
stdenv.mkDerivation rec {
pname = "google-compute-engine-oslogin";
version = "1.5.3";
# from packages/google-compute-engine-oslogin/packaging/debian/changelog
version = "20200325.00";
src = fetchFromGitHub {
owner = "GoogleCloudPlatform";
repo = "compute-image-packages";
rev = "20190522";
sha256 = "16jbbrnz49g843h813r408dbvfa2hicf8canxwbfxr2kzhv7ycmm";
repo = "guest-oslogin";
rev = version;
sha256 = "03hk95pgzcgy6ginp8zdy0fbk88m6n65qq22jq490z1xwbjffm8r";
};
sourceRoot = "source/packages/google-compute-engine-oslogin";
postPatch = ''
# change sudoers dir from /var/google-sudoers.d to /run/google-sudoers.d (managed through systemd-tmpfiles)
substituteInPlace pam_module/pam_oslogin_admin.cc --replace /var/google-sudoers.d /run/google-sudoers.d
substituteInPlace src/pam/pam_oslogin_admin.cc --replace /var/google-sudoers.d /run/google-sudoers.d
# fix "User foo not allowed because shell /bin/bash does not exist"
substituteInPlace compat.h --replace /bin/bash ${bashInteractive}/bin/bash
substituteInPlace src/include/compat.h --replace /bin/bash ${bashInteractive}/bin/bash
'';
buildInputs = [ curl.dev pam ];
@ -31,15 +30,15 @@ stdenv.mkDerivation rec {
NIX_CFLAGS_COMPILE="-I${json_c.dev}/include/json-c";
NIX_CFLAGS_LINK="-L${json_c}/lib";
installPhase = ''
mkdir -p $out/{bin,lib}
install -Dm755 libnss_cache_google-compute-engine-oslogin-${version}.so $out/lib/libnss_cache_oslogin.so.2
install -Dm755 libnss_google-compute-engine-oslogin-${version}.so $out/lib/libnss_oslogin.so.2
install -Dm755 pam_oslogin_admin.so pam_oslogin_login.so $out/lib
install -Dm755 google_{oslogin_nss_cache,authorized_keys} $out/bin
'';
makeFlags = [
"VERSION=${version}"
"DESTDIR=${placeholder "out"}"
"PREFIX=/"
"BINDIR=/bin"
"LIBDIR=/lib"
"PAMDIR=/lib"
"MANDIR=/share/man"
];
enableParallelBuilding = true;