From 8204b1d524b155fac56c3937d1aec41a18ab2d52 Mon Sep 17 00:00:00 2001 From: Matthew Oliver Date: Thu, 23 Jan 2020 11:02:24 +1100 Subject: [PATCH] cephadm: add RGW SSL support This patch adds SSL support to RGW when using cephadm. If an SSL certificate is provided, inside the json supplied with: cpeh orchestrator rgw create -i rgw.json Then the SSL cert and/or key will be added to pushed into the mon config-key database using the key `rgw/cert//.[crt|key]`. Which will then be referenced in the config: rgw_frontends = beast port=80 ssl_port=443 ssl_certificate=config://rgw/cert//.crt And if an ssl key is also supplied this becomes something like: rgw_frontends = beast port=80 ssl_port=443 ssl_certificate=config://rgw/cert//.crt ssl_key=config://rgw/cert//.key Of course you could also just upload the cert and key yourself to config-key location, and ssl will be enabled as well. But this patch let's you either supply them via `-i` or as a manual upload step. Co-Authored-By: Michael Fritch Co-Authored-By: Sebastian Wagner Signed-off-by: Matthew Oliver --- src/cephadm/samples/rgw_ssl.json | 103 ++++++++++++++++++ src/pybind/mgr/cephadm/module.py | 42 +++++-- src/pybind/mgr/orchestrator/module.py | 18 +-- .../ceph/deployment/service_spec.py | 14 +++ 4 files changed, 159 insertions(+), 18 deletions(-) create mode 100644 src/cephadm/samples/rgw_ssl.json diff --git a/src/cephadm/samples/rgw_ssl.json b/src/cephadm/samples/rgw_ssl.json new file mode 100644 index 00000000000..d3c45111a90 --- /dev/null +++ b/src/cephadm/samples/rgw_ssl.json @@ -0,0 +1,103 @@ +{ + "rgw_realm": "default", + "rgw_zone": "default", + "service_type": "rgw", + "placement": { + "hosts": [{ + "hostname": "ironic-moliver", + "name": "", + "network": "" + }], + "count": 1 + }, + "ssl": true, + "rgw_frontend_port": 4343, + "rgw_frontend_ssl_certificate": [ + "-----BEGIN CERTIFICATE-----", + "MIIFmjCCA4KgAwIBAgIJAIZ2n35bmwXTMA0GCSqGSIb3DQEBCwUAMGIxCzAJBgNV", + "BAYTAkFVMQwwCgYDVQQIDANOU1cxHTAbBgNVBAoMFEV4YW1wbGUgUkdXIFNTTCBp", + "bmMuMSYwJAYDVQQDDB1yZ3ctZW5kcG9pbnQuZXhhbXBsZS1jZXBoLmNvbTAeFw0y", + "MDAyMDcwMDEzNTFaFw0zMDAyMDQwMDEzNTFaMGIxCzAJBgNVBAYTAkFVMQwwCgYD", + "VQQIDANOU1cxHTAbBgNVBAoMFEV4YW1wbGUgUkdXIFNTTCBpbmMuMSYwJAYDVQQD", + "DB1yZ3ctZW5kcG9pbnQuZXhhbXBsZS1jZXBoLmNvbTCCAiIwDQYJKoZIhvcNAQEB", + "BQADggIPADCCAgoCggIBAMptGJ523QkEbc37za8iuCTahj0Zr6hy+ToSX/Vfdzxj", + "iYHuD2PiZZyJB7t2eOqiA8sQ5N513EUtf2ZIBwtnnqFIzD5TqI3BxRajUTlOyXUX", + "onMwQwXu2ifDUy3LCmuQfzanOTWvVLac1NmkWbJHpJCXYbUnPb1Nvd0QjTTEH1jt", + "5bDHhfxwCIYK6PY+MqC72a09wB2ZF+EKsSdqghOKmibfJHtoJdsqGeLrysBLrzUJ", + "e/5ZW3V4Z85T2lja5KZnWgRofrUy5TmJV10HO4Hht92xvWvEi/rmjg2AVYZFUQQx", + "xKXpUBbF5T46eSVmaT7IH88Yp5ytgBTaigym7ETCjohp/DfCaK1DUehh0ce7iUq2", + "yCLviZsX4WdPYxzkoLflNrqm4YZP6iKcZSUR/A+qPKCzCXgMXFNA1JxilDwEq35F", + "zGN++ehJqdNmOQ1eQScsLwZQa6mC97d+upWdCvyntf1+S6vNcXhtRQpjNM4W37oW", + "r5nicsGA3/0rpDEHZW85KlkdWO1uCS/6ftgt8UUMaf5ew3PigzusqymBWTlMOjtW", + "uAQXxgZZvkRp+xdspn/uTCAP+bNShGD6Q+TO3U6IjTqHk83sGKCvg2dyU/dqgPr9", + "2IIzgQBFGk0W0nM/E83E8hUSwX17COLL3drhPZb4VRMChQ8PAa6u9nIymkX2wSVv", + "AgMBAAGjUzBRMB0GA1UdDgQWBBSsZHuY7KK80RrZHp+Gx+k16skuRDAfBgNVHSME", + "GDAWgBSsZHuY7KK80RrZHp+Gx+k16skuRDAPBgNVHRMBAf8EBTADAQH/MA0GCSqG", + "SIb3DQEBCwUAA4ICAQAE+BLtnu0p8FtK7vrBCRcCdvycWaSFGJUt7r5Nm8TD7sKw", + "bWeDLgXrRouyA7n6yt/JqQbXYcxt4MLAM0P6NQd5BlNrrnDk4rBnJiJgejppNE+S", + "BazR7Dv0uYcs8kPT4DPpwzv4aJ2aXCBaxYrq8Rx2xOqANCPVOrtPUk9yGpaQ5adU", + "GfxkVbpgIEz1c71PeQuK1KUU/Wpk7cpm+FQCizl9ftP2lHWsGhSLCuyWoMTjt68P", + "gYEWoV54eo/bzwj2ei6TcfNo+uHyzEiiG2qEvMh/cnYUFzs8O1t0mN19WPB1pSh1", + "faci5lGdtkRbLgP0g5RvpagE7Lw3mCc5Om8jmHs4mPfuVkssBVV23CrFpqLLrDX3", + "Acwb/zRGvA7T4WESBTJMYFOLgm0W0Y+AN8RcYNU9QbDhe++Te0uz/3Sy3GN2Xg5z", + "MxfD1+34x6KvMfCh8NjII2mFQ9ukcfrhcfO3oWDLlwsqlVbhkZxNiUOEIx9nzHcF", + "kWpZ2ypBDH45h2o3LyqvGjsu/BFkeG6JpEDCWbClKWcjKxOrLVDufhSDduffDjja", + "zOsgQJg0Yf//Ubb5p0c54GjHM/XDXEcV3m3sEtbmMYz6xGwuag4bx8P2E/QY8sFp", + "JxgIdS8vdl6YhDCjKJ2XzI30JwCdftgDIAiWSE0ivoDc+8+gG1nb11GT52HFzA==", + "-----END CERTIFICATE-----" + ], + "rgw_frontend_ssl_key": [ + "-----BEGIN PRIVATE KEY-----", + "MIIJQwIBADANBgkqhkiG9w0BAQEFAASCCS0wggkpAgEAAoICAQDKbRiedt0JBG3N", + "+82vIrgk2oY9Ga+ocvk6El/1X3c8Y4mB7g9j4mWciQe7dnjqogPLEOTeddxFLX9m", + "SAcLZ56hSMw+U6iNwcUWo1E5Tsl1F6JzMEMF7tonw1MtywprkH82pzk1r1S2nNTZ", + "pFmyR6SQl2G1Jz29Tb3dEI00xB9Y7eWwx4X8cAiGCuj2PjKgu9mtPcAdmRfhCrEn", + "aoITipom3yR7aCXbKhni68rAS681CXv+WVt1eGfOU9pY2uSmZ1oEaH61MuU5iVdd", + "BzuB4bfdsb1rxIv65o4NgFWGRVEEMcSl6VAWxeU+OnklZmk+yB/PGKecrYAU2ooM", + "puxEwo6Iafw3wmitQ1HoYdHHu4lKtsgi74mbF+FnT2Mc5KC35Ta6puGGT+oinGUl", + "EfwPqjygswl4DFxTQNScYpQ8BKt+RcxjfvnoSanTZjkNXkEnLC8GUGupgve3frqV", + "nQr8p7X9fkurzXF4bUUKYzTOFt+6Fq+Z4nLBgN/9K6QxB2VvOSpZHVjtbgkv+n7Y", + "LfFFDGn+XsNz4oM7rKspgVk5TDo7VrgEF8YGWb5EafsXbKZ/7kwgD/mzUoRg+kPk", + "zt1OiI06h5PN7Bigr4NnclP3aoD6/diCM4EARRpNFtJzPxPNxPIVEsF9ewjiy93a", + "4T2W+FUTAoUPDwGurvZyMppF9sElbwIDAQABAoICAQC4sATwP563pXTRpNYq3lCI", + "P2COyqq70/qUA0PNygYt8Nr60srz5RG0WknVvefgm2U+lvFaDsqjyzkbhsf2ndnb", + "aWH/07BLdeluGB/5W2rvDFtJIVVlSmF8OffgJgohzbpjkPrfglKWMkz5LbwwrrD0", + "w0mAUIdB+nYqBfnvlKjNKHCSc9hJU6ZTNg0K7gCfKgUWzOpFlvJ0fp7XSZPYZHL0", + "2E6e0Y0Ig0cPBPb9r4/xoe+hRsHtUafUVik3PK+1K0K0FurUQ9VkQ2yUEg83F0v8", + "Vzht5OuaRVSB+P8O/JtIamfywAY0YOYhepQhjWikwU5UUzhJ+PqNDD87/+g9bA1B", + "xC25eoDxThiQlgDmRoH18ZsWDVf9TuJnm4cpxnZYX6ip+BLm/aidT39auZo0Fl+r", + "cJxRn0Qlm0Vm4Tc/6ZG6PQWB+Q6CjVFdoxeOvEQcTSuKA6VZBStLmqX++5In1Lmj", + "hVr3/aueHiZvXS5bNIdd2IfzatR+nP+uxzM/ryJRvGO2B2XTS00Cvv/lH84BDJYV", + "yt1PJIBoM9Dh7aUAHmKNVfRt83xzvcSPZx9VmSzA6wwqCQcO1GJk6keAuxOuligu", + "YdSFcfChOg90WvBcl+NzMblLkwrFSBQR7kgG0+dedv+Wkm4xO4T7B4W2G5+VIJKG", + "mrEAq6XQMFnfEJzNVg7JUQKCAQEA91eMvphoVVz+cxa4Ew7OokNXk5kSlvmQ8+Ij", + "ngFBvniXPZecxVzFEJglSthH5KI2ZqxwF3GJhKjxUihwf6K13Hx54EM7S/qV57ie", + "kVeKdAs+SGv+hRk1gQOoPBInbtKGKTni1V8T7iNginLueC/YikFugzv6IxiliBSG", + "3R7zjRepOW69aEoCPecx9amU4CkAwgeLJgBloBoqWD8sKM+bl7p5juQCU2sQ9D4/", + "kLnpG9+zPRUNjI4sog3L1wql3zthI6/4gf0TNuDhJTZ68vpMSi02pOUkVa0MmVOA", + "ex16luIp0BhxG/sUAeoevFL4KqR0CBbyAstbt2E/oPYOWMJ4MwKCAQEA0YMNXY7I", + "RNFOmiZ2Wn8kENCyJguqbOMd/li2+ercgp3MaSgTjC5KDFvZqTwXXlrURPu9hcyv", + "sJBSjp45g1T1LsUUq8UJgOIQgxykurIstGg33TAR+TN9VUu/xonLQF23GD8M6Vzd", + "EcZEVlBY33hgNXw4mRcBPnaoG5FZCBfHOgdBCExoYKW/RNKcmu0q+h9uhDBCbopv", + "04ROzw+HW1qc5qvNPR47buZ9+5QdonVK8s2bguMJ0phXwdSxL21wsjIsXyAO9m7w", + "qLHOq/hVokM0Fki09Exg4ppB8cLHC2ITpsVSgn4Dcz5zRtyvhozSKX4R9kMC64a0", + "AgMPVMllmGlR1QKCAQBIIGCrh7gNBIQyvXJKJGw/RxH3uZCBNB9/7vbh8Y3hZDr+", + "PAL8TpQsxaFCVRWJ53+jqy84tuQaKkXM5nv/zEvqEuZbbl+RRW6HVv/udC+srUap", + "Scy7tWEz0QQzGDwlhgCXbwjlnccrD2fsl51QsOsdTf1TCZ9ksqA6sXmua4MsJrUz", + "SUa0bbh/oraf46bFQ0+0RQzftQftixPEDg/rirbdpQQjlfvTpYoZHzncE0qV1ULo", + "UgZUcXU0gH9rovBBy4gFJyB5j3oV67fb6SorRrAOhWbE6QkSbtcYsw/pVuxTqXn1", + "89qwBSSNdl8mHa++h5xKa56BEBobvKEYaAhA+9yfAoIBAQDPFEE5n87Cdj7CjhGd", + "EN2M4Tmz8QPZ7AHRS85O5pxdXaqUpU/s1jPKU16nrwVJ9WypYkjI3q8oTP3MiQc/", + "j9FnENSFkpL6GHdJoB4Rido11myg6spZDVNr4xsCGWATlo1KIceZQHghAV66EWBG", + "QKyXMNigN+S64Hz4AomFPjtkV5cnpJ3mKO0MET9IwfIglsCdVzXSHHK7FaLvdeHL", + "oZxDQrvxFNiZnKgY6SUBVf1mT2LN06n5xSm4I4md3wXsmzrQKtefK7gihNxJjYLW", + "hqYNAIAalwOL9fwIAQTLc30I8S/EWtj+J1O5TpcO3lE7QahvR3yzXsi81Flq7ETG", + "iBKhAoIBAGHGpnjrLlCarNk9axh4Dw1OjgEvwPlEqsiWXt2tylLeab0OGC47MmJx", + "RmKwgVukMuxkQb8v4ANSRtih7R+E+qXfexjEFYtzh/uaRP1Z7ZrO/oqq0oLbPpsx", + "yTSRDL1i5/fgdIlKVH3N4IF7E8Pc3REgYIwLQxYjTdgVHEAM65XegQ2Lkpr4iae3", + "hm4IsD2PrsVITrlsLg65XnfcbsCs/OfQ5GuUp+xUBw5e0bQBmsWEiCaCjrq/EHJa", + "/oeJRqS7lyGYDC+wiSsE70x4dvu1um2F+V1Jw4LWjhu8Z8dNSXPSf8vLqXGkWAlk", + "805lq+iy7Mkhb+dlr4R9WhMWDyGwgYs=", + "-----END PRIVATE KEY-----" + ] +} diff --git a/src/pybind/mgr/cephadm/module.py b/src/pybind/mgr/cephadm/module.py index b730888e5c4..99aba7c9b24 100644 --- a/src/pybind/mgr/cephadm/module.py +++ b/src/pybind/mgr/cephadm/module.py @@ -2763,32 +2763,54 @@ class CephadmOrchestrator(orchestrator.Orchestrator, MgrModule): # ensure rgw_realm and rgw_zone is set for these daemons ret, out, err = self.mon_command({ 'prefix': 'config set', - 'who': 'client.rgw.' + spec.service_id, + 'who': f"{utils.name_to_config_section('rgw')}.{spec.service_id}", 'name': 'rgw_zone', 'value': spec.rgw_zone, }) ret, out, err = self.mon_command({ 'prefix': 'config set', - 'who': 'client.rgw.' + spec.rgw_realm, + 'who': f"{utils.name_to_config_section('rgw')}.{spec.rgw_realm}", 'name': 'rgw_realm', 'value': spec.rgw_realm, }) - if spec.ssl: - v = 'beast ssl_port=%d' % spec.get_port() - else: - v = 'beast port=%d' % spec.get_port() ret, out, err = self.mon_command({ 'prefix': 'config set', - 'who': 'client.rgw.' + spec.service_id, + 'who': f"{utils.name_to_config_section('rgw')}.{spec.service_id}", 'name': 'rgw_frontends', - 'value': v, + 'value': spec.rgw_frontends_config_value(), }) + if spec.rgw_frontend_ssl_certificate: + if isinstance(spec.rgw_frontend_ssl_certificate, list): + cert_data = '\n'.join(spec.rgw_frontend_ssl_certificate) + else: + cert_data = spec.rgw_frontend_ssl_certificate + ret, out, err = self.mon_command({ + 'prefix': 'config-key set', + 'key': f'rgw/cert/{spec.rgw_realm}/{spec.rgw_zone}.crt', + 'val': cert_data, + }) + + if spec.rgw_frontend_ssl_key: + if isinstance(spec.rgw_frontend_ssl_key, list): + key_data = '\n'.join(spec.rgw_frontend_ssl_key) + else: + key_data = spec.rgw_frontend_ssl_key + ret, out, err = self.mon_command({ + 'prefix': 'config-key set', + 'key': f'rgw/cert/{spec.rgw_realm}/{spec.rgw_zone}.key', + 'val': key_data, + }) + + logger.info('Saving service %s spec with placement %s' % ( + spec.service_name(), spec.placement.pretty_str())) + self.spec_store.save(spec) + def _create_rgw(self, rgw_id, host): ret, keyring, err = self.mon_command({ 'prefix': 'auth get-or-create', - 'entity': 'client.rgw.' + rgw_id, - 'caps': ['mon', 'allow rw', + 'entity': f"{utils.name_to_config_section('rgw')}.{rgw_id}", + 'caps': ['mon', 'allow *', 'mgr', 'allow rw', 'osd', 'allow rwx'], }) diff --git a/src/pybind/mgr/orchestrator/module.py b/src/pybind/mgr/orchestrator/module.py index 97a8a496283..20664d18c52 100644 --- a/src/pybind/mgr/orchestrator/module.py +++ b/src/pybind/mgr/orchestrator/module.py @@ -619,11 +619,11 @@ Usage: @_cli_write_command( 'orch daemon add rgw', - 'name=realm_name,type=CephString ' - 'name=zone_name,type=CephString ' + 'name=realm_name,type=CephString,req=false ' + 'name=zone_name,type=CephString,req=false ' 'name=placement,type=CephString,req=false', 'Start RGW daemon(s)') - def _rgw_add(self, realm_name, zone_name, placement=None, inbuf=None): + def _rgw_add(self, realm_name=None, zone_name=None, placement=None, inbuf=None): usage = """ Usage: ceph orch daemon rgw add -i @@ -635,11 +635,13 @@ Usage: except ValueError as e: msg = 'Failed to read JSON input: {}'.format(str(e)) + usage return HandleCommandResult(-errno.EINVAL, stderr=msg) - rgw_spec = RGWSpec( - rgw_realm=realm_name, - rgw_zone=zone_name, - placement=PlacementSpec.from_string(placement), - ) + elif realm_name and zone_name: + rgw_spec = RGWSpec( + rgw_realm=realm_name, + rgw_zone=zone_name, + placement=PlacementSpec.from_string(placement)) + else: + return HandleCommandResult(-errno.EINVAL, stderr=usage) completion = self.add_rgw(rgw_spec) self._orchestrator_wait([completion]) diff --git a/src/python-common/ceph/deployment/service_spec.py b/src/python-common/ceph/deployment/service_spec.py index 7572763d9d0..5bcf7d91a9a 100644 --- a/src/python-common/ceph/deployment/service_spec.py +++ b/src/python-common/ceph/deployment/service_spec.py @@ -523,6 +523,8 @@ class RGWSpec(ServiceSpec): rgw_zone=None, # type: Optional[str] subcluster=None, # type: Optional[str] rgw_frontend_port=None, # type: Optional[int] + rgw_frontend_ssl_certificate=None, # type Optional[List[str]] + rgw_frontend_ssl_key=None, # type: Optional[List[str]] unmanaged=False, # type: bool ssl=False, # type: bool ): @@ -546,6 +548,8 @@ class RGWSpec(ServiceSpec): self.rgw_zone = rgw_zone self.subcluster = subcluster self.rgw_frontend_port = rgw_frontend_port + self.rgw_frontend_ssl_certificate = rgw_frontend_ssl_certificate + self.rgw_frontend_ssl_key = rgw_frontend_ssl_key self.ssl = ssl def get_port(self): @@ -556,6 +560,16 @@ class RGWSpec(ServiceSpec): else: return 80 + def rgw_frontends_config_value(self): + ports = [] + if self.ssl: + ports.append(f"ssl_port={self.get_port()}") + ports.append(f"ssl_certificate=config://rgw/cert/{self.rgw_realm}/{self.rgw_zone}.crt") + ports.append(f"ssl_key=config://rgw/cert/{self.rgw_realm}/{self.rgw_zone}.key") + else: + ports.append(f"port={self.get_port()}") + return f'beast {" ".join(ports)}' + class IscsiServiceSpec(ServiceSpec): def __init__(self, service_id, pool=None, -- 2.39.5