17318
Comment: domtool-admin describe
|
21182
Abstraction to the rescue
|
Deletions are marked like this. | Additions are marked like this. |
Line 23: | Line 23: |
In this example, we'll assume that you've requested and been granted privileges to some Internet domain name (like `you.com`) via our [https://members2.hcoop.net/portal/domain domain permission request form]. If you ''don't'' want to configure a domain that you own, then you don't need to be using DomTool! | In this example, we'll assume that you've requested and been granted privileges to some Internet domain name (like `you.com`) via our [https://members.hcoop.net/portal/domain domain permission request form]. If you ''don't'' want to configure a domain that you own, then you don't need to be using DomTool! |
Line 68: | Line 68: |
Note that using your `~/.domtool` directory as a scratch space with lots of stray files is a bad idea, especially if you have multiple files representing different alternatives for configuring the same domain. Sometimes we make a change to Domtool that requires reprocessing all user configuration. In such cases, we run every file found in any user's `~/.domtool` directory. In that case, your `~/.domtool` files will be run in an arbitrary order, including any files that you have been thinking of as "test cases." It just might happen that the "test cases" end up overriding the "real" files. Also, if ''any'' file in your `~/.domtool` fails to type-check, we will unpublish ''all'' of your domains automatically. In summary, '''use another directory for storing tests''', and run them by specifying explicit filename arguments to `domtool`, possibly with the `-tc` option. | Note that using your `~/.domtool` directory as a scratch space with lots of stray files is a bad idea, especially if you have multiple files representing different alternatives for configuring the same domain. Sometimes we make a change to Domtool that requires reprocessing all user configuration. In such cases, we run every file found in any user's `~/.domtool` directory. In that case, your `~/.domtool` files will be run in an arbitrary order, including any files that you have been thinking of as "test cases." It just might happen that the "test cases" end up overriding the "real" files. In summary, '''use another directory for storing tests''', and run them by specifying explicit filename arguments to `domtool`, possibly with the `-tc` option. |
Line 75: | Line 75: |
If you are having trouble coming up with the right configuration to accomplish something, and if you understand the configuration format of the underlying daemon, then please run `domtool-admin describe` before [https://members2.hcoop.net/portal/support submitting a support request], to see if you'd have the same problem even if you had direct access to underlying daemon configuration. | If you are having trouble coming up with the right configuration to accomplish something, and if you understand the configuration format of the underlying daemon, then please run `domtool-admin describe` before [https://members.hcoop.net/portal/support submitting a support request], to see if you'd have the same problem even if you had direct access to underlying daemon configuration. |
Line 182: | Line 182: |
= Giving Your Fingers a Break = Domtool promotes code re-use using standard techniques from general programming. You can define shortcuts in one file in your `~/.domtool` directory that you can then use from several other files. When you run `domtool` with no arguments, it checks which of your files mention shortcuts defined in other files, and it processes your files in the right order for this to work out right. For instance, say that your home machine has IP address 1.2.3.4. Maybe you find yourself creating many different DNS mappings to that IP address. You could put this code in `~/.domtool/lib.dtl`:{{{ val homeIP = \host -> dnsIP host "1.2.3.4"; }}} Then you could use your shortcut (called a "function" in Domtool and standard programming terminology) in another file in `~/.domtool`:{{{ dom "mydomain" with homeIP "home1"; homeIP "home2" end }}} You could also put the definition of `homeIP` in the same file as the use, at some point before that use but not inside any `dom` directive. You can go further and create a factory for easy churning out of domains with similar configuration. Let's say that you have several domains that differ only in which directory you use to house the web pages for each one. Write this into `~/.domtool/lib.dtl`:{{{ val myDom = \name -> dom name where CreateWWW = false with homeIP "myhouse"; web "www" where ServerAdmin = "me@gmail.com" with serverAliasDefault; alias "/special" (home "shared_special_directory") end end; }}} Now you can use your new directive like:{{{ myDom "mydomain" where DocumentRoot = home "public_html/mydomain" end }}} Besides all the standard stuff that `dom` sets up, `myDom` has given `mydomain` a DNS mapping from `myhouse.mydomain` to 1.2.3.4 and a mapping from `http://www.mydomain/special/` to `~/public_html/mydomain`, a shared directory where you might be storing a standard set of icons or other content you want to reuse across domains. The `DocumentRoot` you set in the use of `myDom` is automatically used by the `web` directive, because it sets an environment variable. You can go even further and write your own directives that takes blocks of configuration as arguments, and then plug them into the resulting configuration in the right places. Here's an expanded version of `myDom` that lets you specify custom configuration for the domain overall:{{{ val myDom = \name -> \\for_domain : Domain -> dom name where CreateWWW = false with homeIP "myhouse"; web "www" where ServerAdmin = "me@gmail.com" with serverAliasDefault; alias "/special" (home "shared_special_directory") end; for_domain end; }}} Now you can use the new directive with a domain-specific DNS mapping:{{{ myDom "mydomain" where DocumentRoot = home "public_html/mydomain" with dnsIP "custom" "4.3.2.1" end }}} In fact, the `dom` directive is implemented in more or less the same way in `~domtool/lib/easy_domain.dtl`. You might also want to make some standard environment variable settings that will persist across your different configuration files. For instance, to make PHP version 5 the default for all of your domains, put this in `lib.dtl`:{{{ PhpVersion = php5; }}} Now this will be the default environment variable setting in all of your domains. While `domtool` does dependency analysis to find the right order for running files that use functions defined in other files with `val`, it doesn't do this for environment variable settings, which is why it's important to put default environment variable settings in `lib.dtl`. The dependency orderer has a special case for files with this name, always putting them first. One final request about this subject: if you come up with function definitions that you think would be useful to other members, post about them on the hcoop-help mailing list, and we may add them to our standard library with your permission. |
This is the DomTool User Guide. The properties of DomTool are described, as well as its use and its configuration file format.
Overview
HCoop is almost unique in the history of the Internet. We are trying to provide highly configurable Internet hosting to the general public while maintaining a high level of security, so that your services keep running and your data stays safe and private. You can probably see that these goals conflict in a number of ways! Since our organization's birth in 2002, we've been evolving tools to help us reconcile these different goals. This document introduces the primary element of our current solution.
This solution is called DomTool, and here's the one-line summary of what it's all about:
DomTool enables HCoop members to use a domain-specific programming language to describe how to configure daemons spread across our servers.
Some other cogent properties of DomTool:
The language is a [http://en.wikipedia.org/wiki/Statically_typed statically-typed], [http://en.wikipedia.org/wiki/Purely_functional purely functional] programming language. Other functional programming languages that you might have heard of include Lisp, Scheme, ML, and Haskell. Haskell and ML are statically-typed, and Haskell is purely functional.
The language has a rich and extensible type system that can enforce complicated security policies through type-checking.
Configuration works via a distributed client/server architecture, so, with the right software installed, you can configure your domains from anywhere.
Some readers may be scared off by this level of technical jargon. We apologize, but we just couldn't help showing it off. The rest of this document will be aimed at the average member, assuming only a solid understanding of the basics of Linux and the Internet. In particular, we assume no experience with particular kinds of programming.
Configuration files
In this example, we'll assume that you've requested and been granted privileges to some Internet domain name (like you.com) via our [https://members.hcoop.net/portal/domain domain permission request form]. If you don't want to configure a domain that you own, then you don't need to be using DomTool!
You will edit a text file with the configuration directives that you want applied your domain. Throughout this document, $USER is your HCoop username, $DOMAIN is your domain name, and you should write configuration into a file ~$USER/.domtool/$DOMAIN. If you're logged into one of our servers, cd ~/.domtool should get you to the proper directory.
Your home directory is something like /afs/hcoop.net/user/u/us/username, where you should replace username with your HCoop username and u and us with the one- and two-character prefixes of that username. Be sure that your .domtool directory has an AFS ACL set that allows the user domtool to read it, or your domains may go down when we need to reload everyone's configuration. If you end up with the wrong permissions, see MemberManual/GettingStarted/AfsExamples for the right command to run.
We leave it up to you to choose how to edit the source file. You might run something like emacs $DOMAIN over SSH, or you might open it locally if you have our AFS cell mounted in your local filesystem. In the latter case, those of you lucky enough to be using UNIX-based home systems will probably want to create softlinks from your local home directories to your HCoop home directories in AFS, to save some typing each time you want to open Domtool files.
In this file $DOMAIN, you can write: {{{dom "$DOMAIN" with end;}}}
Remember, you should replace $DOMAIN with the actual domain name.
This example introduces the simplest useful configuration, based on the dom directive. What does this source file accomplish? It:
- Declares our nameservers as authoritative for your domain.
- Declares that Exim should handle e-mail for your domain.
- Directs Exim to send all mail to your domain to your mailbox.
Adds a DNS mapping from yourdomain.com to our web server's IP address.
Adds a DNS mapping from www.yourdomain.com to our web server's IP address.
Creates an Apache virtual host at that hostname serving ~$USER/public_html/.
There are more primitive configuration directives to set up each of these features individually. dom packages all of the functionality together in an easy-to-use package.
One of the main principles behind DomTool and our use of it is to avoid configuration repetition wherever possible. The DomTool language contains several abstraction features familiar from high-level programming languages, as well as some new ones, all in support of abstracting common code patterns into programming language entities that can be called multiple times. In general, if you find yourself writing the same code over and over again, let us know, and we'll try to add a new abstraction to our standard library! You could even write such an abstraction yourself, but that's beyond the scope of this intro document.
Now, enough talk; let's execute our source file! Assuming that you're in your .domtool directory, run
domtool $DOMAIN
This contacts our central dispatcher server over an SSL connection. Once the server verifies that you are who you say you are, it will publish your configuration to the affected daemons, and you should be able to start using your domain.
You could also simply run
domtool
which calls the dispatcher with all configuration files in your .domtool directory, regardless of what your current working directory is. This can be a helpful shortcut sometimes, though it can be noticeably less efficient than calling domtool with the single domain that you know you've changed.
In fact, the names of the source files in your .domtool directory don't matter. We recommend storing the configuration for each of your domains in a separate file named after that domain, but you might want to make different choices. For instance, you might want to use an abstraction that configures multiple domains at once. However, it is important that you keep all your permanent configuration files in your .domtool directory. Sometimes we need to reprocess all configuration from scratch, and in such cases our dispatcher will only look for source files in your .domtool directory. For temporary experimentation, though, you can feel free to store source files elsewhere and run them explicitly with
domtool $FILENAME
Debugging configuration files
To check a configuration file without actually publishing its results, run:
domtool -tc $FILENAME
Here, -tc stands for "type-check." The Domtool language is designed to capture all rules of which configuration is valid and which isn't in its parsing and typing rules, so using the -tc option should enable you to find any bugs in your configuration. We might have bugs in our implementations of the configuration directives from time to time, but -tc should be sufficient to help you find your own bugs.
Note that using your ~/.domtool directory as a scratch space with lots of stray files is a bad idea, especially if you have multiple files representing different alternatives for configuring the same domain. Sometimes we make a change to Domtool that requires reprocessing all user configuration. In such cases, we run every file found in any user's ~/.domtool directory. In that case, your ~/.domtool files will be run in an arbitrary order, including any files that you have been thinking of as "test cases." It just might happen that the "test cases" end up overriding the "real" files. In summary, use another directory for storing tests, and run them by specifying explicit filename arguments to domtool, possibly with the -tc option.
Debugging for experts
Many of our members are quite familiar with the native configuration formats of standard UNIX daemons like Apache and BIND. For them, we provide a facility for seeing which "real" configuration is being sent to those daemons as a result of Domtool configuration files. To see all published configuration for $DOMAIN, run:
domtool-admin describe $DOMAIN
If you are having trouble coming up with the right configuration to accomplish something, and if you understand the configuration format of the underlying daemon, then please run domtool-admin describe before [https://members.hcoop.net/portal/support submitting a support request], to see if you'd have the same problem even if you had direct access to underlying daemon configuration.
Permissions
Now let's put on our Evil Hacker from the Seventh Circle of Hell hats. If you write this to a file hcoop.net: {{{dom "hcoop.net" with end;}}} and run:
domtool hcoop.net
you should see an error message like: {{{hcoop.net:0.0-1.14:error: Function argument has wrong type.
- Expression: "hcoop.net"
Actual type: string Needed type: your_domain}}}
What this is saying is that you are only allowed to use dom with domains that you are allowed to configure. You tried to configure hcoop.net, which is not one of those domains, and so is treated like an arbitrary string (sequence of characters). The type checker has saved the day, and the Evil Hacker is prevented from mucking with hcoop.net configuration.
How exactly does DomTool determine which domains you're allowed to configure? It uses a general permissions system based on access control lists. You can list all of your permissions by running:
domtool-admin perms
You should see output like this: {{{Permissions for you: domain: you.com you.net you.org path: /afs/hcoop.net/user/y/yo/you user: you}}} where you stands for your username. The domain list gives the Internet domains to which you've been granted configuration rights. user lists the UNIX users as whom you may run programs, and path gives the filesystem paths that you're allowed to reference in your configurations. You have rights to all subdirectories of path entries, too.
You might like to perform some other queries on the permissions database, too. For instance, you might like to know which member owns someone.com. You could run:
domtool-admin whohas domain someone.com
and hopefully get back a reply like:
whohas domain / someone.com: someone
In general, running
domtool-admin whohas $CLASS $VALUE
will list every user who has $VALUE in the $CLASS row of his permissions table.
Nested configuration directives
Let's look under the hood at what dom is doing by writing an equivalent configuration file that doesn't use it. {{{domain "$DOMAIN" with
- dns (dnsNS "ns1.hcoop.net"); dns (dnsNS "ns3.hcoop.net"); dns (dnsA "www" web_ip); handleMail; catchAllAlias "$USER"; vhost "www" with end;
end;}}}
Now we see what that with..end syntax was about: it relates to nested configuration directives. Both dom and the more primitive domain directive take additional configuration specific to the named domain. Everything we include here inside domain could also have been included inside a dom use, though it would be redundant.
The contents of the domain block are a series of DomTool language expressions that evaluate to actions. Each action is given using consistent programming language syntax, rather than the ad-hoc configuration syntaxes used by daemons like Apache and Exim. This means that the directives are sometimes slightly more verbose, but they are much easier for humans and machines to parse, since no directive-specific ad-hoc syntax information is required.
Let's step through the nested directives one by one to explain their meanings.
The dns lines register DNS mappings to be included in $DOMAIN's zone. dnsNS records list the authoritative nameservers in order. dnsA records provide mappings from hostnames to IP addresses. web_ip is a variable containing the IP address of our default web server for member use, defined in the standard library.
handleMail asks Exim to provide relaying for any e-mail message addressed to any address at $DOMAIN.
catchAllAlias "$USER" directs Exim to deposit any mail addressed to any user at $DOMAIN in $USER's mailbox.
The vhost directive demonstrates several layers of nested configuration. It declares an Apache virtual host named www.$DOMAIN. We could have included further Apache-specific configuration inside the vhost directive.
Configuration contexts
Not all configuration directives make sense in all contexts. For instance, it doesn't make sense to specify a URL rewrite rule outside of a vhost directive. The DomTool language uses a concept of contexts or predicates to enforce correct usage of directives.
To illustrate, let's try breaking the rule we just gave in our example: {{{dom "hcoop.net" with
- alias "/doc" "/usr/local/doc"
end;}}}
We ask for the URI prefix doc/ to be mapped to file path /usr/local/doc. The alias directive is only allowed inside vhosts, so we get an error like this: {{{hcoop.net:0.0-3.4:error: Context incompatibility for nested action. Have: Domain Need: Vhost}}}
This one's simple enough. We're in a Domain, but we need to be in a Vhost to use that directive. In full generality, DomTool's context specification language is a small logic that can be used to express much more complicated conditions, while maintaining the ability for the interpreter to check proper usage automatically. See the DomTool/LanguageReference for a full specification.
Environment variables
DomTool has its own concept of typed environment variables that are used to tweak parameters of configuration directives. Here's an example to demonstrate how they're used and why they're useful.
Running this configuration: {{{vhost "www" with end;}}} gives you an Apache virtual host that serves documents out of ~/public_html. If you wanted to serve documents out of $DIR instead, you could run: {{{vhost "www" where
DocumentRoot = "$DIR"
with end;}}} Maybe you want to use the same document root for two different virtual hosts. In that case, you could use: