Skip to content

graygnuorg/sargon

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

39 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Sargon

Sargon authorizes requests to a dockerd daemon using access control lists defined in a LDAP database or in its configuration file.

Used with due proficiency, Sargon mitigates security risks that appear when running dockerd in a multi-user environment.

Installation

Building

After cloning the repository, change to the source directory and run

 make

To install the created binary, run (as root):

 make install

By default, the sargon binary is installed to /usr/local/bin. To select another installation directory, use the BINDIR or PREFIX variable. The BINDIR variable specifies the directory to install sargon to. E.g. to install it to /usr/bin, do

 make install BINDIR=/usr/bin

Alternatively, you may use the PREFIX variable, which specifies the directory where bin is located, e.g.:

 make install PREFIX=/usr

Create the configuration file

Sargon provides sufficiently sane defaults that allow it to be run without explicit configuration file. These defaults are:

  1. Upon startup, the PID of the running process is stored in file /var/run/sargon.pid.

  2. ACLs are stored in an LDAP database.

  3. The LDAP configuration file is looked up in the following locations:

  • /etc/ldap.conf
  • /etc/ldap/ldap.conf
  • /etc/openldap/ldap.conf
  1. Anonymous user name is ANONYMOUS.

If these don't suit your needs, create the file /etc/docker/sargon.json. Using the configuration file description, edit the Sargon settings to your liking.

The above defaults correspond to the following sargon.json file:

{
    "PidFile":"/var/run/sargon.pid",
    "LdapConf":"/etc/ldap.conf:/etc/ldap/ldap.conf:/etc/openldap/ldap.conf",
    "AnonymousUser":"ANONYMOUS"
}

In the discussion below we assume you will be using the LDAP database to store Sargon ACLs. If it's not the case and your intent is to keep all ACLs in the configuration file and disable LDAP altogether, use the following configuration file:

{
    "LdapConf":""
}

(setting LdapConf to an empty string disables LDAP).

In this case you can skip the section that follows.

Configure the LDAP database

Include the Sargon schema into your slapd configuration.

Most slapd installations nowadays use dynamic configuration system. In that case, import the sargon.ldif file into your configuration:

 ldapadd -f sargon.ldif

Depending on your setup, you may need additional options to ldapadd

If using the legacy configuration, copy the file sargon.schema to the schema subdirectory of your slapd configuration directory. Depending on the installation, it is /etc/openldap/schema or /etc/ldap/schema. Include it in your slapd configuration, by adding the following statement to your slapd.conf:

 include         /etc/openldap/schema/sargon.schema

Restart slapd.

Create the root object for Sargon ACL hierarchy:

ldapadd <<EOF
dn: ou=sargon,dc=example,dc=com
objectClass: organizationalUnit
ou: sargon
description: root for Sargon (Docker ACL) objects.
EOF

Create the default policy entry. It's an ACL entry that will be used when no other object matches the request. The built-in Sargon defaults are very (perhaps even overly) strict, that's why such an entry is needed so that you can continue using docker while building and tuning your ACLs:

ldapadd <<EOF
dn: cn=default-policy,ou=sargon,dc=example,dc=com
cn: default-policy
objectClass: sargonACL
sargonUser: ANONYMOUS
sargonAllow: ALL
sargonOrder: 100
EOF

(don't forget to replace dc=example,dc=org with your actual base dn.)

This entry allows anonymous users to issue any docker requests. The sargonOrder attribute ensures it will be placed at the end of the constructed access control list.

Of course it is only a minimal example. You can modify this entry as needed or even remove it altogether.

An alternative to keeping the default in the LDAP database is storing it in the configuration file.

Storing ACLs in the configuration file

If you prefer to keep the default policy entry, or even entire ACL, in the configuration file, use the ACL attribute. The value of this attribute is a list of access control entries stored as JSON object. For example, the default policy discussed above is stored in the configuration file as:

{
    "ACL":[
      {
          "Id":"default policy",
	  "User":["ANONYMOUS"],
	  "Allow":["ALL"],
	  "Order":100
      }
    ]
}

The rules for converting ACL objects from LDAP to JSON are:

  1. Replace cn attribute with Id.
  2. Replace each sargon* attribute with its name without the sargon prefix. E.g. sargonUser becomes User, etc. Notice that LDAP attribute names are case-insensitive, whereas JSON attribute names aren't. Use the canonical attribute names when performing conversion.
  3. Use scalar value if attribute is marked as (single) in the attribute list, otherwise use array.
  4. Upper-case TRUE and FALSE become lower-case (e.g. for the sargonAllowPrivileged attribute).
  5. Ensure proper ordering of objects in the ACL array, either by using Order attributes or by explicitly ordering the entries. Remember that Sargon will use the first entry that matches the request.

Configure Docker to use Sargon

Add the following attribute to the file /etc/docker/daemon.json:

 "authorization-plugins": ["sargon"]

Otherwise, add the following option to your dockerd command line:

  --authorization-plugin=sargon

If you opt for the latter variant, the exact place where to apply it depends on how dockerd is started on your system. If your system uses systemd (which is the case for most modern GNU/Linux distributions, such as Ubuntu, Debian, etc), first extract the actual dockerd command line:

systemctl show --property=ExecStart --value docker | sed -e 's/.*argv\[\]=//' -e 's/;.*//'

Then run

systemctl edit docker

In the text editor it starts, type the following:

[Service]
ExecStart=
ExecStart=COMMAND --authorization-plugin=sargon

where COMMAND is the command line you obtained in the previous step. Notice the empty ExecStart= at the start of the section. It is mandatory.

Save your changes and exit the editor.

Adding your first ACL entry

To illustrate the effect of Sargon ACLs on docker, let's add to the LDAP database an object that will control what directories anonymous users are allowed to mount in containers. Using your editor, create the following ldif file:

dn: cn=anon,ou=sargon,dc=example,dc=com
cn: anon
objectClass: sargonACL
sargonUser: ANONYMOUS
sargonMount: /var/lib/mounts/*

The sargonUser attribute declares that this entry applies to anonymous users only. The sargonMount attribute says that only subdirectories of /var/lib/mounts on host machine are allowed to be mounted in containers.

Add your changes to the database:

 ldapadd -f file.ldif

Now, if you try to run

 docker run -v /etc:/usr/local/etc debian:10

you will get the following error:

docker: Error response from daemon: authorization denied by plugin sargon: mounting /etc is not allowed.

If sargon is running in trace mode (--trace option), you will see the following in its logs:

[TRACE] ANONYMOUS: binding to /etc is rejected by default policy

On the other hand, running

 docker run -v /var/lib/mounts/src:/usr/src debian:10

will succeed. Sargon trace in this case will contain:

[TRACE] ANONYMOUS: binding to /var/lib/mounts/src is accepted by cn=anon,ou=sargon,dc=example,dc=com

Usage

When started, the program reads its configuration file, disconnects itself from the controlling terminal and continues running in the background. Error reporting goes to the syslog facility daemon. Both short (single-dash) and GNU-style long (double-dash) options are supported. The following options are recognized:

  • -f, --foreground

    Run in the foreground. Send diagnostic output to the stderr.

  • -c, -config=FILE

    Read configuration from FILE instead of the default /etc/docker/sargon.json

  • -t, --trace

    Trace ACL entries and their effect.

  • -d, --debug

    Enable verbose debugging output.

  • -h, --help

    Produce a short command line usage summary and exit.

  • -v, --version

    Print program version, short copying information, and exit.

Configuration

Sargon configuration is kept in JSON format in file /etc/docker/sargon.json. The following keywords are recognized:

  • PidFile

    Name of the PID file. Defaults to /var/run/sargon.pid.

  • LdapConf

    Colon-separated list of LDAP configuration files to look for. The first of them that exists will be read. The default is /etc/ldap.conf:/etc/ldap/ldap.conf:/etc/openldap/ldap.conf

    To disable LDAP, set this attribute to an empty string.

  • LdapUser

    Bind to LDAP using this DN. Defaults to empty string. This value overrides the binddn setting in ldap.conf file, which is the preferred way of configuring bind DN.

  • LdapPass

    Bind to LDAP with this password. Defaults to empty string. This value overrides the password obtained from the bindpwfile setting in ldap.conf file.

  • LdapTLS

    If true, start LDAP TLS session.

  • AnonymousUser

    If docker connection is not authenticated, use this string as the user name.

  • ACL

    A list of ACL entries stored in JSON format. This list will be appended to the list obtained from LDAP before final sorting of entries, or used alone if LDAP database is disabled or is unreachable. This makes it useful for storing default policies.

The ldap.conf file

After reading its main configuration file, sargon scans the LDAP configuration path (see the LdapConf variable above). The first file that exists and is readable is read. The format of the file is described in detail in ldap.conf(5). The following keywords are recognized:

  • URI ldap[si]://[name[:port]]

    URI of an LDAP server to which sargon should connect.

  • BASE base

    Specifies the default base DN to use when performing ldap queries.

  • BINDDN dn

    Specifies the default bind DN to use when performing ldap operations.

  • BINDPWFILE filename

    Use the content of filename as the password for simple authentication. Note, that the file is read verbatim and is not parsed in any way. In particular, beware of trailing newlines.

  • TLS_CACERT filename

    Specifies the file that contains certificates for all of the Certificate Authorities the client will recognize.

  • TLS_CACERTDIR dirname

    Specifies the path of a directory that contains Certificate Authority certificates in separate individual files. The TLS_CACERT is always used before TLS_CACERTDIR.

  • TLS_CERT filename

    Specifies the file that contains client certificate.

  • TLS_KEY filename

    Specifies the file that contains the private key for the certificate stored in the TLS_CERT file.

  • TLS_RANDFILE filename

    Specifies the file to obtain random bits from, instead of the default /dev/urandom or /dev/random.

  • TLS_REQCERT level

    Specifies what checks to perform on server certificates in a TLS session, if any. The level is one of: never, allow, try, demand, or try.

ACLs

Privileges for docker users are defined in the LDAP database in form of sargonACL objects. Each such object defines privileges for a set of users performing certain docker action on a set of servers. Each sargonACL object must have the cn attribute, uniquely identifying the object. It may also have one or more of the following attributes. Except as marked with (single), multiple attribute instances are allowed.

  • sargonUser

    User to whom this entry applies. If the value begins with a percent sign, the rest of characters is treated as the name of a user group and the entry applies to all users in this group.

  • sargonHost

    Host on which this entry takes effect. If the value starts with a plus sign, it is treated as the name of the NIS netgroup.

  • sargonAllow

    Allowed action. The value must be one of the docker action keywords, or the word ALL (uppercase) matching all actions.

  • sargonDeny

    Denied action. The value must be one of the docker action keywords, or the word ALL (uppercase) matching all actions. See below for a detailed discussion on how sargonAllow and sargonDeny policies operate.

  • sargonOrder (single)

    An integer used to order multiple sargonACL entries. If not present, 0 is assumed.

  • sargonMount

    Name of the directory on the host filesystem that is allowed for mounting inside a container. The value of this attribute is treated as a globbing pattern. Before use, it undergoes variable expansion: any variable references in form $V or ${V} are replaced with the actual value of variable V. The following variables are defined:

    Variable Expands to
    uid User ID
    gid Group ID
    name User name
    home or dir Home directory

    Undefined variables are left unexpanded.

    For example:

    • sargonMount:/var/lib/mounts

      Allow to mount only /var/lib/mounts

    • sargonMount:/var/lib/mounts/*

      Allow to mount any directories under /var/lib/mounts

    The value can end with a list of flags in parentheses. The following flags are recognized:

    • ro

      Allow read-only mounting. Attempts to mount the directory for writing will be rejected.

    • globlex

      Use lexical globbing: the * wildcard matches any sequence of characters, including directory separators (slashes) and the ? wildcard matches any character, including slash. This is the default.

    • globpath

      Use pathname globbing: * and ? don't match slash.

    • globstar

      Use star globbing. As with globpath neither * nor ? will match a slash character. The ** wildcard is provided, which matches zero or more arbitrary characters, including slashes.

    Some more examples:

    • sargonMount:/var/lib/mounts/*(ro,globpath)

      Allow to mount only subdirectories of /var/lib/mounts and only for reading.

    • sargonMount:/var/*/mounts/**(globstar)

      Allow to mount directories located at any depth under the mounts directory in any subdirectory of /var. Thus, mounting /var/lib/mounts/foo/bar will be allowed, whereas mounting /var/lib/sub/mounts/foo/bar will not.

  • sargonAllowPrivileged (single)

    The word TRUE if the object allows creation of privileged containers. FALSE otherwise.

  • sargonMaxMemory (single)

    Maximum size of the memory the container is allowed to use. The value is an integer optionally suffixed with K, M, or G (case-insensitive).

  • sargonMaxKernelMemory (single)

    Limit on kernel memory usage. The value is an integer optionally suffixed with K, M, or G (case-insensitive).

  • sargonAllowCapability

    Name of the linux capability that is allowed to use with the --cap-add docker option. See capabilities(7), for a list of capability names. Names listed in this attribute are case-insensitive. The CAP_ prefix is optional.

  • sargonNotBefore

    A timestamp in the form yyyymmddHHMMSSZ that provides a start date/time for when this entry will be valid. Notice, that the timestamp must be in UTC.

  • sargonNotAfter

    A timestamp in the form yyyymmddHHMMSSZ that provides an expiration date/time after which this entry ceases to be valid. Notice, that the timestamp must be in UTC.

Actions

The following values can be used in sargonAllow and sargonDeny attributes:

  • BuildPrune Delete builder cache.

  • ConfigCreate Create a config.

  • ConfigDelete Delete a config.

  • ConfigInspect Inspect a config.

  • ConfigList List configs.

  • ConfigUpdate Update a config.

  • ContainerArchive Get an archive of a filesystem resource in a container.

  • ContainerArchiveInfo Get information about files in a container.

  • ContainerAttach Attach to a container.

  • ContainerAttachWebsocket Attach to a container via a websocket.

  • ContainerChanges Get changes on a container’s filesystem.

  • ContainerCreate Create a container.

  • ContainerDelete Remove a container.

  • ContainerExec Create an exec instance.

  • ContainerExport Export a container.

  • ContainerInspect Inspect a container.

  • ContainerKill Kill a container.

  • ContainerList List containers.

  • ContainerLogs Get container logs.

  • ContainerPause Pause a container.

  • ContainerPrune Delete stopped containers.

  • ContainerRename Rename a container.

  • ContainerResize Resize a container TTY.

  • ContainerRestart Restart a container.

  • ContainerStart Start a container.

  • ContainerStats Get container stats based on resource usage.

  • ContainerStop Stop a container.

  • ContainerTop List processes running inside a container.

  • ContainerUnpause Unpause a container.

  • ContainerUpdate Update a container.

  • ContainerWait Wait for a container.

  • DistributionInspect Get image information from the registry.

  • ExecInspect Inspect an exec instance.

  • ExecResize Resize an exec instance.

  • ExecStart Start an exec instance.

  • GetPluginPrivileges Get plugin privileges.

  • ImageBuild Build an image.

  • ImageCommit Create a new image from a container.

  • ImageCreate Create an image.

  • ImageDelete Remove an image.

  • ImageGet Export an image.

  • ImageGetAll Export several images.

  • ImageHistory Get the history of an image.

  • ImageInspect Inspect an image.

  • ImageList List Images.

  • ImageLoad Import images.

  • ImagePrune Delete unused images.

  • ImagePush Push an image.

  • ImageSearch Search images.

  • ImageTag Tag an image.

  • NetworkConnect Connect a container to a network.

  • NetworkCreate Create a network.

  • NetworkDelete Remove a network.

  • NetworkDisconnect Disconnect a container from a network.

  • NetworkInspect Inspect a network.

  • NetworkList List networks.

  • NetworkPrune Delete unused networks.

  • NodeDelete Delete a node.

  • NodeInspect Inspect a node.

  • NodeList List nodes.

  • NodeUpdate Update a node.

  • PluginCreate Create a plugin.

  • PluginDelete Remove a plugin.

  • PluginDisable Disable a plugin.

  • PluginEnable Enable a plugin.

  • PluginInspect Inspect a plugin.

  • PluginList List plugins.

  • PluginPull Install a plugin.

  • PluginPush Push a plugin.

  • PluginSet Configure a plugin.

  • PluginUpgrade Upgrade a plugin.

  • PutContainerArchive Extract an archive of files or folders to a directory in a container.

  • SecretCreate Create a secret.

  • SecretDelete Delete a secret.

  • SecretInspect Inspect a secret.

  • SecretList List secrets.

  • SecretUpdate Update a Secret.

  • ServiceCreate Create a service.

  • ServiceDelete Delete a service.

  • ServiceInspect Inspect a service.

  • ServiceList List services.

  • ServiceLogs Get service logs.

  • ServiceUpdate Update a service.

  • Session Initialize interactive session.

  • SwarmInit Initialize a new swarm.

  • SwarmInspect Inspect swarm.

  • SwarmJoin Join an existing swarm.

  • SwarmLeave Leave a swarm.

  • SwarmUnlock Unlock a locked manager.

  • SwarmUnlockkey Get the unlock key.

  • SwarmUpdate Update a swarm.

  • SystemAuth Check auth configuration.

  • SystemDataUsage Get data usage information.

  • SystemEvents Monitor events.

  • SystemInfo Get system information.

  • SystemPing Ping.

  • SystemVersion Get version.

  • TaskInspect Inspect a task.

  • TaskList List tasks.

  • TaskLogs Get task logs.

  • VolumeCreate Create a volume.

  • VolumeDelete Remove a volume.

  • VolumeInspect Inspect a volume.

  • VolumeList List volumes.

  • VolumePrune Delete unused volumes.

Request processing

When authorizing incoming requests, sargon uses the following algorithm:

  1. Create LDAP filter with the user name and the names of the groups the user belongs to. For example, if the requesting user name is smt, and this user is member of the groups staff, docker, and wheel, then the LDAP filter will be:
      (&(objectClass=sargonACL)
        (|(sargonUser=smt)
          (sargonUser=ALL)
          (sargonUser=%staff)
          (sargonUser=%docker)
          (sargonUser=%wheel)))

Notice, that (1) the filter string is split in multiple indented lines for readability, and (2) the filter normally contains conditions that control validity of the entry using the sargonNotBefore and sargonNotAfter attributes. These conditions are omitted for clarity.

  1. Execute LDAP query, get the response.

  2. Iterate over the returned sargonACL objects, selecting only those with the value of sargonHost attribute matching the server hostname, or (if the value starts with +) with the netgroup that matches the (host,user,domain) triplet.

    To match the netgroup, the libc function innetgr(3) is used.

  3. Sort the remaining entries by the value of their sargonOrder attribute in ascending order.

  4. Start with the first returned object.

  5. If the requested docker action is explicitly listed in one of its sargonAllow attributes, go to step 9.

  6. Otherwise, if the object has one or more sargonDeny attributes and one of these contains the requested action or the meta-action ALL, then deny the request.

  7. Advance to the next object, and restart from step 6.

  8. Unless the requested action is ContainerCreate or VolumeCreate, authorize the request.

  9. For VolumeCreate requests, check if the requested mountpoint satisfies the sargonMount attribute. Authorize the request is so and reject it otherwise.

The steps below are followed when processing ContainerCreate requests

  1. If creation of a privileged container is requested, consult the sargonAllowPrivileged attribute. If its value is FALSE, deny the request. Otherwise, advance to the next step.

  2. If any additional linux capabilities are requested, check if all of them are listed in sargonAllowCapability attributes. If not, deny the request.

  3. Check the requested binds and mounts. Check each source directory against each sargonMount attribute. If the directory matches, mounting is allowed. Otherwise, deny the request.

  4. If the requested maximum memory is greater than the value of the sargonMaxMemory attribute, the request is denied.

  5. If the requested maximum kernel memory is greater than the value of the sargonMaxKernelMemory attribute, the request is denied.

  6. Otherwise, the request is authorized.