mirror of
https://pagure.io/fm-orchestrator.git
synced 2026-04-05 03:38:12 +08:00
Add simple auth based on the client certificate and pkgdb API.
This commit is contained in:
61
cacert.pem
Normal file
61
cacert.pem
Normal file
@@ -0,0 +1,61 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIK6zCCBt+gAwIBAgIJAMXcvWMyB9ZeMA0GCSqGSIb3DQEBBQUAMIGxMQswCQYD
|
||||
VQQGEwJVUzEXMBUGA1UECBMOTm9ydGggQ2Fyb2xpbmExEDAOBgNVBAcTB1JhbGVp
|
||||
Z2gxFzAVBgNVBAoTDkZlZG9yYSBQcm9qZWN0MRowGAYDVQQLExFGZWRvcmEgUHJv
|
||||
amVjdCBDQTEaMBgGA1UEAxMRRmVkb3JhIFByb2plY3QgQ0ExJjAkBgkqhkiG9w0B
|
||||
CQEWF2FkbWluQGZlZG9yYXByb2plY3Qub3JnMB4XDTA4MDgyMDE0NDkxNloXDTE4
|
||||
MDgxODE0NDkxNlowgbExCzAJBgNVBAYTAlVTMRcwFQYDVQQIEw5Ob3J0aCBDYXJv
|
||||
bGluYTEQMA4GA1UEBxMHUmFsZWlnaDEXMBUGA1UEChMORmVkb3JhIFByb2plY3Qx
|
||||
GjAYBgNVBAsTEUZlZG9yYSBQcm9qZWN0IENBMRowGAYDVQQDExFGZWRvcmEgUHJv
|
||||
amVjdCBDQTEmMCQGCSqGSIb3DQEJARYXYWRtaW5AZmVkb3JhcHJvamVjdC5vcmcw
|
||||
ggQWMA0GCSqGSIb3DQEBAQUAA4IEAwAwggP+AoID9QDIH2F1s0y5V7xBc2tHlXOA
|
||||
H7999QZ76BU1qtDg4g4k2KyYTG7Gk5eNnJntbpYtRNPL0bQymJIhcfkMCER+UOfv
|
||||
mum6hrwYSrb0ehsIP1mY9QXdJnlvA1ViXMpZy74byaue9Rn+9GOaOtRWv9dZ5/j4
|
||||
Wf9JDOt7TzgFfTPZrtasqlSaOicWJuAKyp2SkQup3I0fTtM4/LpR6BY+dDr7ud9d
|
||||
LTukkGuOPnNx1pxKkuN0jKYwZjwUcQHlRUNF5xrARU5youYSD7ReWdJsZkirJ0W2
|
||||
dZkUQaIUm55v3p4soMYnbPeJFoAbSJkqSCPI4c/ex/Xr1xp3dXvd0vi9K+w8tvw1
|
||||
Q3XUvQxum97dbcM7Sw3gRfpFy6K3Up+xXaEnMDGhX31zQAHFTP/P7N+CWNwLg57r
|
||||
EmuYVfP31b6qsyvuLnpMqe0fYRNWOiJYMALPyRT15RSFGaLyKevqqzR5DFmHQI2C
|
||||
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
|
||||
YPF6KxAYMs2ema7PGl2D
|
||||
-----END CERTIFICATE-----
|
||||
@@ -10,3 +10,9 @@ rpms_default_repository = git://pkgs.stg.fedoraproject.org/rpms/
|
||||
rpms_allow_repository = False
|
||||
rpms_default_cache = http://pkgs.stg.fedoraproject.org/repo/pkgs/
|
||||
rpms_allow_cache = False
|
||||
|
||||
ssl_certificate_file=server.crt
|
||||
ssl_certificate_key_file=server.key
|
||||
ssl_ca_certificate_file=cacert.pem
|
||||
|
||||
pkgdb_api_url=https://admin.stg.fedoraproject.org/pkgdb/api
|
||||
|
||||
17
rida.py
17
rida.py
@@ -36,9 +36,10 @@ This is the implementation of the orchestrator's public RESTful API.
|
||||
# TODO: Emit messages about module submission.
|
||||
|
||||
from flask import Flask, request
|
||||
from rida import config, database, messaging
|
||||
from rida import config, database, messaging, auth
|
||||
import json
|
||||
import modulemd
|
||||
import ssl
|
||||
|
||||
app = Flask(__name__)
|
||||
app.config.from_envvar("RIDA_SETTINGS", silent=True)
|
||||
@@ -50,6 +51,12 @@ db = database.Database()
|
||||
@app.route("/rida/module-builds/", methods=["POST"])
|
||||
def submit_build():
|
||||
"""Handles new module build submissions."""
|
||||
|
||||
username = auth.is_packager(conf.pkgdb_api_url)
|
||||
if not username:
|
||||
return ("You must use your Fedora certificate when submitting"
|
||||
" new build", 403)
|
||||
|
||||
try:
|
||||
r = json.loads(request.data.decode('utf-8'))
|
||||
except:
|
||||
@@ -126,4 +133,10 @@ def query_build(id):
|
||||
return "No such module found.", 404
|
||||
|
||||
if __name__ == "__main__":
|
||||
app.run()
|
||||
ssl_ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2)
|
||||
ssl_ctx.load_cert_chain(conf.ssl_certificate_file,
|
||||
conf.ssl_certificate_key_file)
|
||||
ssl_ctx.verify_mode = ssl.CERT_OPTIONAL
|
||||
ssl_ctx.load_verify_locations(cafile=conf.ssl_ca_certificate_file)
|
||||
|
||||
app.run(request_handler=auth.ClientCertRequestHander, ssl_context=ssl_ctx)
|
||||
|
||||
80
rida/auth.py
Normal file
80
rida/auth.py
Normal file
@@ -0,0 +1,80 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2016 Red Hat, Inc.
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in all
|
||||
# copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
#
|
||||
# Written by Jan Kaluza <jkaluza@redhat.com>
|
||||
|
||||
"""Auth system based on the client certificate and FAS account"""
|
||||
|
||||
from flask import Flask, request
|
||||
from werkzeug.serving import WSGIRequestHandler
|
||||
import requests
|
||||
import json
|
||||
|
||||
class ClientCertRequestHander(WSGIRequestHandler):
|
||||
"""
|
||||
WSGIRequestHandler subclass adding SSL_CLIENT_CERT_* variables
|
||||
to `request.environ` dict when the client certificate is set and
|
||||
is signed by CA configured in `conf.ssl_ca_certificate_file`.
|
||||
"""
|
||||
|
||||
def make_environ(self):
|
||||
environ = WSGIRequestHandler.make_environ(self)
|
||||
|
||||
try:
|
||||
cert = self.request.getpeercert(False)
|
||||
except:
|
||||
cert = None
|
||||
|
||||
if cert and "subject" in cert:
|
||||
for keyval in cert["subject"]:
|
||||
key, val = keyval[0]
|
||||
environ["SSL_CLIENT_CERT_" + key] = val
|
||||
return environ
|
||||
|
||||
def is_packager(pkgdb_api_url):
|
||||
"""
|
||||
Returns the username of user associated with current request by checking
|
||||
client cert's commonName and pkgdb database API.
|
||||
|
||||
When user is not a packager (is not in pkgdb), returns None.
|
||||
"""
|
||||
if not "SSL_CLIENT_CERT_commonName" in request.environ:
|
||||
return None
|
||||
|
||||
username = request.environ["SSL_CLIENT_CERT_commonName"]
|
||||
|
||||
acl_url = pkgdb_api_url + "/packager/package/" + username
|
||||
|
||||
resp = requests.get(acl_url)
|
||||
try:
|
||||
resp.raise_for_status()
|
||||
except:
|
||||
return None
|
||||
|
||||
try:
|
||||
r = json.loads(resp.content.decode('utf-8'))
|
||||
except:
|
||||
return None
|
||||
|
||||
if r["output"] == "ok":
|
||||
return username
|
||||
|
||||
return None
|
||||
@@ -56,6 +56,12 @@ def from_file(filename=None):
|
||||
conf.rpms_allow_repository = default.getboolean("rpms_allow_repository")
|
||||
conf.rpms_default_cache = default.get("rpms_default_cache")
|
||||
conf.rpms_allow_cache = default.getboolean("rpms_allow_cache")
|
||||
|
||||
conf.ssl_certificate_file = default.get("ssl_certificate_file")
|
||||
conf.ssl_certificate_key_file = default.get("ssl_certificate_key_file")
|
||||
conf.ssl_ca_certificate_file = default.get("ssl_ca_certificate_file")
|
||||
|
||||
conf.pkgdb_api_url = default.get("pkgdb_api_url")
|
||||
return conf
|
||||
|
||||
class Config(object):
|
||||
@@ -72,6 +78,10 @@ class Config(object):
|
||||
self._rpms_allow_repository = False
|
||||
self._rpms_default_cache = ""
|
||||
self._rpms_allow_cache = False
|
||||
self._ssl_certificate_file = ""
|
||||
self._ssl_certificate_key_file = ""
|
||||
self._ssl_ca_certificate_file = ""
|
||||
self._pkgdb_api_url = ""
|
||||
|
||||
@property
|
||||
def system(self):
|
||||
@@ -170,3 +180,35 @@ class Config(object):
|
||||
if not isinstance(b, bool):
|
||||
raise TypeError("rpms_allow_cache must be a bool.")
|
||||
self._rpms_allow_cache = b
|
||||
|
||||
@property
|
||||
def ssl_certificate_file(self):
|
||||
return self._ssl_certificate_file
|
||||
|
||||
@ssl_certificate_file.setter
|
||||
def ssl_certificate_file(self, s):
|
||||
self._ssl_certificate_file = str(s)
|
||||
|
||||
@property
|
||||
def ssl_ca_certificate_file(self):
|
||||
return self._ssl_ca_certificate_file
|
||||
|
||||
@ssl_ca_certificate_file.setter
|
||||
def ssl_ca_certificate_file(self, s):
|
||||
self._ssl_ca_certificate_file = str(s)
|
||||
|
||||
@property
|
||||
def ssl_certificate_key_file(self):
|
||||
return self._ssl_certificate_key_file
|
||||
|
||||
@ssl_certificate_key_file.setter
|
||||
def ssl_certificate_key_file(self, s):
|
||||
self._ssl_certificate_key_file = str(s)
|
||||
|
||||
@property
|
||||
def pkgdb_api_url(self):
|
||||
return self._pkgdb_api_url
|
||||
|
||||
@pkgdb_api_url.setter
|
||||
def pkgdb_api_url(self, s):
|
||||
self._pkgdb_api_url = str(s)
|
||||
|
||||
Reference in New Issue
Block a user