How Traditional Client Authentication works - uyuni-project/uyuni GitHub Wiki

Right now original Spacewalk clients have access to some web endpoints using a mix of login via XML-RPC + tokens via HTTP headers.

The System Id file (XML)

The objective of the system id file is to be able to do XML-RPC calls to the server from the client without requiring the admin password. The client has to demonstrate the server it is the same client that registered the first time.

These calls are used eg. to upload hardware information, update the package profile, change channel subscriptions and are called from actions and the client agent code.

Initial Creation of the System Id file

  • During registration of a client, the server is created in the DB together with a secret for it (see column 'secret' in rhnServer table). A "system id" XML file, is created in /etc/sysconfig/rhn/systemid, containing:

  • the numeric client id

  • some fields with info

  • a validation checksum, calculated from the fields and the client specific secret

The idea is that presenting this XML file to the server, it can know both the client ID plus that the server himself was who wrote that file (because of the checksum).

Authentication using the System Id XML file

Authentication of XML-RPC endpoints specific to the client

As an example, the spacewalk-zypp-plugin has a hook that after a transaction (eg. installing packages) it updates the package profile on the server. The plugin calls the following function from the client /usr/share/rhn/up2date_client/rhnPackageInfo.py

rhnPackageInfo.updatePackageProfile()

This function calls:

s.registration.update_packages(up2dateAuth.getSystemId(), packages)

Which is a proxy for the XML-RPC endpoint on the server. On the server side this ends in /usr/lib/python2.7/site-packages/spacewalk/server/handlers/xmlrpc/registration.py

def update_packages(self, system_id, packages):
  # ...   
  server = self.auth_system(system_id)
  #...

The server object is protected by authentication with the system id. This call is redirected to /usr/lib/python2.7/site-packages/spacewalk/rhnHandler.py

def auth_system(self, system_id):
  # ...
  server = rhnServer.get(system_id, load_user=self.load_user)
  if not server:
  # Invalid server certificate.
  raise rhnFault(9, _(
     "Please run rhn_register as root on this client"))
  # ...        

So the rhnServer on the server side has a getter based on the system id certificate. In /usr/lib/python2.7/site-packages/spacewalk/server/rhnServer/init.py :

def get(system_id, load_user=1):
  # ...
  cert = Certificate()
  if not cert.reload(system_id) == 0:
    return None
  # if invalid, stop here
  if not cert.valid():
        return None
  # ...
  # this looks like a real server
  server = Server(None)
  # and load it up
  if not server.loadcert(cert, load_user) == 0:
    return None
  # okay, it is a valid certificate
  return server

So if the cert is valid, the loadcert() function loads the data for the ID in the certificate, and the server object is returned. The certificate validation code is in /usr/lib/python2.7/site-packages/spacewalk/server/rhnServer/server_certificate.py and it is basically a XML file with the ID of the server, some hardware/OS related fields and a checksum which is basically a MAC constructed from those fields plus a secret available in the database entry of the client (and created during registration).

Authentication of HTTP endpoints (eg. repositories)

To avoid sending the systemid on every request, what the client does is, once "logins" (XML-RPC) into Spacewalk by sending this full XML file, which is verified on the server as described above.

If things match, the login() returns a token as a set of HTTP headers that the client can use to access repositories, something like:

X-RHN-Server-Id: 100001
X-RHN-Server-Time: timestamp
X-RHN-Server-Time-Offset: 1 hour
X-RHN-Server-Auth: XXXXXXXXXXXXXXXXXXXXXXXXXX

X-RHN-Server-Auth is now constructed from those fields plus a server-wide secret.

The client then send these fields plus X-RHN-Auth token on every HTTP request and the servers verifies it on every request together with the expiration. The verification happens in /usr/lib/python2.7/site-packages/spacewalk/server/apacheAuth.py on the server.

This is for example how the package manager configures the HTTP requests to download packages. First login() using the system id, get the X-RHN-* headers and set them for the download requests. Example.