Merge pull request #86319 from flokli/google-oslogin
nixos/google-oslogin: bump package, make tests more readable
This commit is contained in:
commit
c2c30d926c
3 changed files with 86 additions and 44 deletions
|
@ -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'"
|
||||
)
|
||||
'';
|
||||
})
|
||||
|
|
|
@ -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'')
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
Loading…
Reference in a new issue