This is a draft of a document on the architecture of ceo.
All of these components are available in source form in the the public/pyceo.git repository. The official Debian packages are on http://debian.csclub.uwaterloo.ca/. This server should be in the APT sources list of all club machines.
The ceo ecosystem consists of the following components:
- a command-line interface to various club-related administation functions
- a Kerberized daemon responsible for executing tasks requiring special access (e.g. creating Kerberos principals or MySQL databases) on behalf of the user
- a client program used by ceo to send messages to ceod for execution
ceo is the user interface with which users interact. It is implemented in Python and presents a curses-based menu system (though some features can also be accessed by passing command-line flags). It is installed at /usr/bin/ceo by the ceo-python Debian package.
When launched, users may be prompted for their password. This is used to obtain a Kerberos ticket for the LDAP service if neither a service ticket nor ticket-granting ticket are found in the cache.
The following tasks are completed by creating a request which is relayed by ceoc and executed by ceod on the appropriate machine.
- Adding new users (members, club reps, clubs) is done on ginseng.
- Creating MySQL databases is done on caffeine since mysqld is configured to allow only local connections.
The library feature queries a PostgreSQL database; all other functions (at time of writing) are done over LDAP using the user's previously obtained ticket.
The interface itself consists of a series of menus and wizards using urwid which call the appropriate functions in the ceo package (such as ceo.members.create_member).
ceod is installed at /usr/sbin/ceod by the ceo-daemon package. It is a Kerberized daemon (service principal ceod/hostname@CSCLUB.UWATERLOO.CA) listening on TCP port 9987. Each connection forks a slave ceod process to handle that session.
ceod's basic message format is this (note that network byte order is big-endian, whereas the most common host architectures are little-endian):
|msglen||32-bit unsigned integer (network order)||Length of the message body, in bytes|
|msgtype||32-bit unsigned integer (network order)||Type of message (MSG_AUTH = 0x8000000, all other values are treated as an op message)|
|body||Raw bytes||Body of the message|
Clients are expected to authenticate (using MSG_AUTH messages) before attempting to execute operations. Failure to do so will result in the connection being unceremoniously terminated. The body of an authentication message is a GSSAPI (Kerberos) token. These are used according to the Kerberos protocol to authenticate the client to ceod (and vice versa) and establish a session key.
The msgtype of an op message identifies the operation to be run. This is a reference to the appropriate op as defined in a configuration file in /etc/csc/ops. One such file is /etc/csc/ops/adduser:
ginseng adduser root 0x01
The first field here is the host on which the operation should be executed. ceod will not execute an operation that is not configured to execute on the host. The second field is the the op name. The third field is the user/group as which the operation should execute. The fourth field is the msgtype which represents that op.
If a local op is found which matches the msgtype of the message, the corresponding op executable (located using the op name) is run as the requested user/group. For instance, the adduser op would run /usr/lib/ceod/op-adduser as root.
Operations can be implemented in any language, but are currently implemented in C and Python. Some headers and source files in the ceo source may be useful if writing one in C; the ceo package (particularly ceo.ops) may be useful in Python.
The body of the message may be obtained by reading from the standard input pipe. The CEO_CONFIG_DIR environment variable contains the ceo configuration directory; the CEO_USER environment variable contains the username of the authenticated user. It is important to note that while ceod does authenticate the user, it does not perform any authorization whatsoever. So, while you can rely on the user being who ceod says they are, all ops should check whether the user is authorized to perform the action (for instance, by checking their membership in a group, or by only performing that operation only on the user requesting it).