]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
auth/cephx: cap ticket validity by expiration of "next" key
authorIlya Dryomov <idryomov@gmail.com>
Thu, 15 Apr 2021 07:48:13 +0000 (09:48 +0200)
committerIlya Dryomov <idryomov@gmail.com>
Fri, 16 Apr 2021 14:16:24 +0000 (16:16 +0200)
If auth_mon_ticket_ttl is increased by several times as done in
commit 522a52e6c258 ("auth/cephx: rotate auth tickets less often"),
active clients eventually get stuck because the monitor sends out an
auth ticket with a bogus validity.  The ticket is secured with the
"current" secret that is scheduled to expire according to the old TTL,
but the validity of the ticket is set to the new TTL.  As a result,
the client simply doesn't attempt to renew, letting the secrets rotate
potentially more than once.  When that happens, the client first hits
auth authorizer errors as it tries to renew service tickets and when
it finally gets to renewing the auth ticket, it hits the insecure
global_id reclaim wall.

Cap TTL by expiration of "next" key -- the "current" key may be
milliseconds away from expiration and still be used, legitimately.
Do it in KeyServerData alongside key rotation code and propagate the
capped TTL to the upper layer.

Signed-off-by: Ilya Dryomov <idryomov@gmail.com>
(cherry picked from commit 370c9b13970d47a55b1b20ef983c6f01236c9565)

Conflicts:
src/auth/cephx/CephxKeyServer.cc [ commit ef3c42cd6481 ("auth:
  EACCES, not EPERM") not in nautilus ]

src/auth/cephx/CephxKeyServer.cc
src/auth/cephx/CephxKeyServer.h
src/auth/cephx/CephxServiceHandler.cc

index dccee44d9a7dfb6e6983f25f0351b8408b43b2c8..700e4abf78b5e24642bb32a292717fe7329cd7d0 100644 (file)
@@ -22,7 +22,8 @@
 #define dout_prefix *_dout << "cephx keyserverdata: "
 
 bool KeyServerData::get_service_secret(CephContext *cct, uint32_t service_id,
-                                      CryptoKey& secret, uint64_t& secret_id) const
+                                      CryptoKey& secret, uint64_t& secret_id,
+                                      double& ttl) const
 {
   map<uint32_t, RotatingSecrets>::const_iterator iter =
        rotating_secrets.find(service_id);
@@ -39,15 +40,25 @@ bool KeyServerData::get_service_secret(CephContext *cct, uint32_t service_id,
   if (secrets.secrets.size() > 1)
     ++riter;
 
-  if (riter->second.expiration < ceph_clock_now())
+  utime_t now = ceph_clock_now();
+  if (riter->second.expiration < now)
     ++riter;   // "current" key has expired, use "next" key instead
 
   secret_id = riter->first;
   secret = riter->second.key;
 
+  // ttl may have just been increased by the user
+  // cap it by expiration of "next" key to prevent handing out a ticket
+  // with a bogus, possibly way into the future, validity
+  ttl = service_id == CEPH_ENTITY_TYPE_AUTH ?
+      cct->_conf->auth_mon_ticket_ttl : cct->_conf->auth_service_ticket_ttl;
+  ttl = min(ttl, static_cast<double>(
+                    secrets.secrets.rbegin()->second.expiration - now));
+
   ldout(cct, 30) << __func__ << " service "
                 << ceph_entity_type_name(service_id) << " secret_id "
-                << secret_id << " " << riter->second << dendl;
+                << secret_id << " " << riter->second << " ttl " << ttl
+                << dendl;
   return true;
 }
 
@@ -223,12 +234,12 @@ bool KeyServer::get_caps(const EntityName& name, const string& type,
   return data.get_caps(cct, name, type, caps_info);
 }
 
-bool KeyServer::get_service_secret(uint32_t service_id,
-               CryptoKey& secret, uint64_t& secret_id) const
+bool KeyServer::get_service_secret(uint32_t service_id, CryptoKey& secret,
+                                  uint64_t& secret_id, double& ttl) const
 {
   std::scoped_lock l{lock};
 
-  return data.get_service_secret(cct, service_id, secret, secret_id);
+  return data.get_service_secret(cct, service_id, secret, secret_id, ttl);
 }
 
 bool KeyServer::get_service_secret(uint32_t service_id,
@@ -402,12 +413,13 @@ bool KeyServer::get_service_caps(const EntityName& name, uint32_t service_id,
 
 int KeyServer::_build_session_auth_info(uint32_t service_id,
                                        const AuthTicket& parent_ticket,
-                                       CephXSessionAuthInfo& info)
+                                       CephXSessionAuthInfo& info,
+                                       double ttl)
 {
   info.service_id = service_id;
   info.ticket = parent_ticket;
-  info.ticket.init_timestamps(ceph_clock_now(),
-                             cct->_conf->auth_service_ticket_ttl);
+  info.ticket.init_timestamps(ceph_clock_now(), ttl);
+  info.validity.set_from_double(ttl);
 
   generate_secret(info.session_key);
 
@@ -425,13 +437,14 @@ int KeyServer::build_session_auth_info(uint32_t service_id,
                                       const AuthTicket& parent_ticket,
                                       CephXSessionAuthInfo& info)
 {
-  if (!get_service_secret(service_id, info.service_secret, info.secret_id)) {
+  double ttl;
+  if (!get_service_secret(service_id, info.service_secret, info.secret_id,
+                         ttl)) {
     return -EPERM;
   }
 
   std::scoped_lock l{lock};
-
-  return _build_session_auth_info(service_id, parent_ticket, info);
+  return _build_session_auth_info(service_id, parent_ticket, info, ttl);
 }
 
 int KeyServer::build_session_auth_info(uint32_t service_id,
@@ -444,6 +457,7 @@ int KeyServer::build_session_auth_info(uint32_t service_id,
   info.secret_id = secret_id;
 
   std::scoped_lock l{lock};
-  return _build_session_auth_info(service_id, parent_ticket, info);
+  return _build_session_auth_info(service_id, parent_ticket, info,
+                                 cct->_conf->auth_service_ticket_ttl);
 }
 
index 2a342e30302aaec321bb4f27c7a4c453b2801fea..390e2e4f17077f8da9a5c5968646b82553ef25d2 100644 (file)
@@ -96,7 +96,8 @@ struct KeyServerData {
   }
 
   bool get_service_secret(CephContext *cct, uint32_t service_id,
-                         CryptoKey& secret, uint64_t& secret_id) const;
+                         CryptoKey& secret, uint64_t& secret_id,
+                         double& ttl) const;
   bool get_service_secret(CephContext *cct, uint32_t service_id,
                          uint64_t secret_id, CryptoKey& secret) const;
   bool get_auth(const EntityName& name, EntityAuth& auth) const;
@@ -201,7 +202,8 @@ class KeyServer : public KeyStore {
   void _dump_rotating_secrets();
   int _build_session_auth_info(uint32_t service_id, 
                               const AuthTicket& parent_ticket,
-                              CephXSessionAuthInfo& info);
+                              CephXSessionAuthInfo& info,
+                              double ttl);
   bool _get_service_caps(const EntityName& name, uint32_t service_id,
        AuthCapsInfo& caps) const;
 public:
@@ -225,8 +227,8 @@ public:
                              uint64_t secret_id);
 
   /* get current secret for specific service type */
-  bool get_service_secret(uint32_t service_id, CryptoKey& service_key, 
-                         uint64_t& secret_id) const;
+  bool get_service_secret(uint32_t service_id, CryptoKey& secret,
+                         uint64_t& secret_id, double& ttl) const;
   bool get_service_secret(uint32_t service_id, uint64_t secret_id,
                          CryptoKey& secret) const override;
 
index 82c964e47fa02d506d7b8a983ccba15d7693f743..886014e8bb9af4c09a6ad6df6bc7a019b602a1e1 100644 (file)
@@ -210,11 +210,20 @@ int CephxServiceHandler::handle_request(
        break;
       }
 
-      info.ticket.init_timestamps(ceph_clock_now(),
-                                 cct->_conf->auth_mon_ticket_ttl);
+      double ttl;
+      if (!key_server->get_service_secret(CEPH_ENTITY_TYPE_AUTH,
+                                         info.service_secret, info.secret_id,
+                                         ttl)) {
+        ldout(cct, 0) << " could not get service secret for auth subsystem" << dendl;
+        ret = -EIO;
+        break;
+      }
+
+      info.service_id = CEPH_ENTITY_TYPE_AUTH;
       info.ticket.name = entity_name;
       info.ticket.global_id = global_id;
-      info.validity += cct->_conf->auth_mon_ticket_ttl;
+      info.ticket.init_timestamps(ceph_clock_now(), ttl);
+      info.validity.set_from_double(ttl);
 
       key_server->generate_secret(session_key);
 
@@ -222,12 +231,6 @@ int CephxServiceHandler::handle_request(
       if (psession_key) {
        *psession_key = session_key;
       }
-      info.service_id = CEPH_ENTITY_TYPE_AUTH;
-      if (!key_server->get_service_secret(CEPH_ENTITY_TYPE_AUTH, info.service_secret, info.secret_id)) {
-        ldout(cct, 0) << " could not get service secret for auth subsystem" << dendl;
-        ret = -EIO;
-        break;
-      }
 
       vector<CephXSessionAuthInfo> info_vec;
       info_vec.push_back(info);
@@ -288,7 +291,6 @@ int CephxServiceHandler::handle_request(
                service_id,
                info.ticket,
                svc_info);
-             svc_info.validity += cct->_conf->auth_service_ticket_ttl;
              info_vec.push_back(svc_info);
            }
          }
@@ -358,7 +360,6 @@ int CephxServiceHandler::handle_request(
            service_err = r;
            continue;
          }
-          info.validity += cct->_conf->auth_service_ticket_ttl;
           info_vec.push_back(info);
          ++found_services;
         }