I’ve implemented custom web-based frontends that offer a limited set of domain manipulation functions to a limited set of users multiple times. I usually divide those up into two components:
- The web frontend, usually a CGI script written in whichever scripting language I’m most comfortable with (Perl, Ruby for me).
- A backend script that receives a very limited set of parameters from the web frontend script and translates those into domain modification calls (either modifying the LDAP directory directly or by calling the
udm command-line tool), again written whatever language you want to.
There’s a very good reason for using two components: security. All CGI scripts usually run as the user the web server runs as, which is
www-data, a non-privileged user. In order to modify the LDAP you need some kind of elevated privileges (e.g.
udm uses the machine account and needs access to
/etc/machine.secret which is only readable by
root). One could give
www-data that access, but the huge drawback is that suddenly all CGI scripts, all PHP scripts etc. all have that access. And all too often arbitrary users can upload arbitrary PHP files to be interpreted by the web server (e.g. via
/home/username/public_html which is then accessible as
Hence the split. I usually allow the user
www-data to execute the backend script with arbitrary parameters via
In order to be really secure, you must implement all verification in the backend script (and some in both the backend and the frontend script). For example, if you want to restrict access to that tool to members of the group
user admins, you will have to implement that in both scripts. The frontend script should show some kind of login screen and error out if the credentials provided don’t belong to a user who is a member of said group.
However, those same credentials must be passed on to the backend script which has to check the same. If the backend script didn’t verify credentials and relied on the frontend script to do so, any other rogue PHP/CGI script could simply pretend to make that check and call the backend script in order to make changes the calling rogue script shouldn’t be allowed to trigger.
As a communication channel I usually use temporary files containing JSON. I only pass the name of the temporary file to the backend script — for simplicity reasons. JSON is easy to read and write (use existing libraries, don’t implement your own parser, of course), you don’t have to worry about command line parameters being visible in the process list or in any history files etc.
I can usually whip up something simple within a day. Depending on the scope of functionality this can take an arbitrary amount of time, of course.
What I like about implementing your own solution is that you’re much more aware of where the security boundaries are and what the consequences of violating them are. Your frontend usually offers a very small number of functions with a small number of parameters. Validating them in the backend is therefore straightforward and fast. For example: users created by your
user admins must always be members of certain groups? Easy, don’t offer the choice of which groups to add to in the frontend in the first place and hardcode them in the backend. Or you want to allow certain groups? Great, hardcode that list in the backend and make sure the “additional groups” passed from the frontend are all members of said list.
Using a real programming/scripting language also allows you to express arbitrary constraints — unlike LDAP ACLs.
Another real benefit is that your frontend only offers those controls/attributes that may actually be changed or set by the
user admins members. This is unlike the UMC which would always offer you the full “edit user” mask, no matter what LDAP ACLs allow and prohibit. It’s very confusing to figure out which parameters you may or may not change in the UMC because it seems to suggest you may change each and every of those.