Initial commit
This commit is contained in:
commit
70ffd9b09c
8 changed files with 374 additions and 0 deletions
3
.dockerignore
Normal file
3
.dockerignore
Normal file
|
@ -0,0 +1,3 @@
|
|||
docker-compose.yml
|
||||
.git/
|
||||
data/
|
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
data/
|
27
Dockerfile
Normal file
27
Dockerfile
Normal file
|
@ -0,0 +1,27 @@
|
|||
FROM debian:stretch
|
||||
EXPOSE 389
|
||||
|
||||
ENV DEBIAN_FRONTEND noninteractive \
|
||||
CONFDIR /etc/ldap/slapd.d \
|
||||
DATADIR /var/lib/ldap
|
||||
|
||||
# add our users and groups first to ensure their IDs get assigned consitently
|
||||
RUN groupadd -r -g 500 openldap && useradd -r -u 500 -g openldap openldap
|
||||
|
||||
|
||||
RUN \
|
||||
apt-get update && \
|
||||
apt-get install --yes --no-install-recommends \
|
||||
slapd \
|
||||
ldap-utils \
|
||||
ca-certificates && \
|
||||
# remove the default config, since the entrypoint
|
||||
# will populate it by hand.
|
||||
rm -rf /etc/ldap/slapd.d && \
|
||||
apt-get clean && \
|
||||
rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
|
||||
|
||||
COPY entrypoint.sh /entrypoint.sh
|
||||
COPY fixtures/ /usr/share/slapd/fixtures/
|
||||
|
||||
ENTRYPOINT ["/entrypoint.sh"]
|
30
README.md
Normal file
30
README.md
Normal file
|
@ -0,0 +1,30 @@
|
|||
## slapd
|
||||
Slapd offers a LDAP server, which we mostly use for authentication of various
|
||||
services.
|
||||
Therefore, a lot of services have a direct (or indirect) dependency on this
|
||||
image.
|
||||
|
||||
### Building slapd
|
||||
|
||||
```
|
||||
docker build -t zombi/slapd .
|
||||
```
|
||||
|
||||
### Running slapd
|
||||
|
||||
```
|
||||
docker run -d --name ldap -v /data/ldap:/data -p 389:389 zombi/slapd
|
||||
```
|
||||
|
||||
### Backing up data
|
||||
Data in this container is considered essential, since it influences almost
|
||||
all other services we run.
|
||||
|
||||
We recently discovered that **simply copying all the data from `data` DOES
|
||||
NOT WORK**, therefore we included scripts for backing up the slapd database
|
||||
into a compact .ldif format.
|
||||
|
||||
running `tools/create-ldap-backup.sh` will create two files:
|
||||
* `conf.ldif` is a backup of the configuration.
|
||||
* `data.ldif` contains all the saved datasets.
|
||||
|
15
docker-compose.yml
Normal file
15
docker-compose.yml
Normal file
|
@ -0,0 +1,15 @@
|
|||
version: '2'
|
||||
|
||||
services:
|
||||
ldap:
|
||||
build: .
|
||||
ports:
|
||||
- "3810:389"
|
||||
environment:
|
||||
- "ROOTPW=pass"
|
||||
- "ORGANIZATION=zombi"
|
||||
- "DATADIR=/data"
|
||||
- "CONFDIR=/conf"
|
||||
volumes:
|
||||
- ./data/ldap/config:/conf
|
||||
- ./data/ldap/data:/data
|
93
entrypoint.sh
Executable file
93
entrypoint.sh
Executable file
|
@ -0,0 +1,93 @@
|
|||
#!/bin/bash -e
|
||||
set -eo pipefail
|
||||
|
||||
# set default values for undefined vars
|
||||
CONFDIR=${CONFDIR:-'/etc/ldap/slapd.d'}
|
||||
DATADIR=${DATADIR:-'/var/lib/ldap'}
|
||||
|
||||
if [[ -z "$ORGANIZATION" && -z "$SUFFIX" ]] ; then
|
||||
fail 'neither ORGANIZATION nor SUFFIX supplied as environment var'
|
||||
fi
|
||||
|
||||
if [[ -n "$ORGANIZATION" ]] ; then
|
||||
# if an organization was set, we can generate the RootDN from that
|
||||
SUFFIX=${SUFFIX:-"o=${ORGANIZATION}"}
|
||||
fi
|
||||
|
||||
function fail {
|
||||
# write to stderr
|
||||
echo "ERROR: $@" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
function configure {
|
||||
sed \
|
||||
-e "s|@SUFFIX@|${SUFFIX}|g" \
|
||||
-e "s|@PASSWORD@|${ROOTPW}|g" \
|
||||
-e "s|@DATADIR@|${DATADIR}|g" \
|
||||
/usr/share/slapd/fixtures/config.ldif \
|
||||
| slapadd -F "$CONFDIR" -b "cn=config"
|
||||
|
||||
return $?
|
||||
}
|
||||
|
||||
function init_fixtures {
|
||||
mkdir -p /docker-entrypoint-initdb.d
|
||||
# if no files exist in this directory then copy example structure
|
||||
if ! ls /docker-entrypoint-initdb.d/* 1> /dev/null 2>&1 ; then
|
||||
cp /usr/share/slapd/fixtures/example_structure.ldif /docker-entrypoint-initdb.d/structure.ldif
|
||||
fi
|
||||
|
||||
for f in /docker-entrypoint-initdb.d/*; do
|
||||
|
||||
case "$f" in
|
||||
*.sh)
|
||||
echo "$0: running $f"; . "$f"
|
||||
;;
|
||||
*.ldif)
|
||||
sed \
|
||||
-e "s|@SUFFIX@|${SUFFIX}|g" \
|
||||
-e "s|@PASSWORD@|${ROOTPW}|g" \
|
||||
-e "s|@DATADIR@|${DATADIR}|g" \
|
||||
-e "s|@ORGANIZATION@|${ORGANIZATION}|g" \
|
||||
"$f" \
|
||||
| slapadd -F "$CONFDIR" -b "$SUFFIX"
|
||||
;;
|
||||
*) echo "$0: ignoring $f" ;;
|
||||
esac
|
||||
echo
|
||||
done
|
||||
|
||||
}
|
||||
|
||||
# Reduce maximum number of open file descriptors to 1024
|
||||
# otherwise slapd consumes two orders of magnitude more of RAM
|
||||
# see https://github.com/docker/docker/issues/8231
|
||||
ulimit -n 1024 || fail "could not set ulimits"
|
||||
|
||||
# create dirs if they not already exists
|
||||
[ -d $CONFDIR ] || mkdir -p $CONFDIR
|
||||
[ -d $DATADIR ] || mkdir -p $DATADIR
|
||||
|
||||
if [[ ! -d "$CONFDIR/cn=config" ]] ; then
|
||||
echo "configuration not found, creating one for $SUFFIX .."
|
||||
[[ -z "$ROOTPW" ]] && fail "ROOTPW not set."
|
||||
[[ -z "$SUFFIX" ]] && fail "SUFFIX not set."
|
||||
|
||||
# if rootpw is not already in hashed format, hash it first
|
||||
[[ "${ROOTPW:0:1}" == '{' ]] \
|
||||
||ROOTPW=`slappasswd -s "$ROOTPW"`
|
||||
|
||||
configure || fail "could not create slapd config"
|
||||
fi
|
||||
|
||||
if [[ ! -f "$DATADIR/data.mdb" ]] ; then
|
||||
init_fixtures
|
||||
fi
|
||||
|
||||
# fix file permissions
|
||||
chown -R openldap:openldap $CONFDIR $DATADIR \
|
||||
|| fail "could not change permissions"
|
||||
|
||||
echo "Starting slapd."
|
||||
exec /usr/sbin/slapd -F $CONFDIR -u openldap -g openldap -h 'ldapi:// ldap://' -d stats
|
156
fixtures/config.ldif
Normal file
156
fixtures/config.ldif
Normal file
|
@ -0,0 +1,156 @@
|
|||
# this file was adapted from the default /usr/share/slapd/slapd.init.ldif
|
||||
# Global config:
|
||||
dn: cn=config
|
||||
objectClass: olcGlobal
|
||||
cn: config
|
||||
olcPidFile: /var/run/slapd/slapd.pid
|
||||
# List of arguments that were passed to the server
|
||||
olcArgsFile: /var/run/slapd/slapd.args
|
||||
# Read slapd-config(5) for possible values
|
||||
olcLogLevel: none
|
||||
# The tool-threads parameter sets the actual amount of cpu's that is used
|
||||
# for indexing.
|
||||
olcToolThreads: 1
|
||||
|
||||
# Frontend settings
|
||||
dn: olcDatabase={-1}frontend,cn=config
|
||||
objectClass: olcDatabaseConfig
|
||||
objectClass: olcFrontendConfig
|
||||
olcDatabase: {-1}frontend
|
||||
# The maximum number of entries that is returned for a search operation
|
||||
olcSizeLimit: 500
|
||||
# Allow unlimited access to local connection from the local root user
|
||||
olcAccess: {0}to * by dn.exact=gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth manage by * break
|
||||
# Allow unauthenticated read access for schema and base DN autodiscovery
|
||||
olcAccess: {1}to dn.exact="" by * read
|
||||
olcAccess: {2}to dn.base="cn=Subschema" by * read
|
||||
|
||||
# Config db settings
|
||||
dn: olcDatabase=config,cn=config
|
||||
objectClass: olcDatabaseConfig
|
||||
olcDatabase: config
|
||||
# Allow unlimited access to local connection from the local root user
|
||||
olcAccess: to * by dn.exact=gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth manage by * break
|
||||
olcRootDN: cn=admin,cn=config
|
||||
olcRootPW: @PASSWORD@
|
||||
|
||||
# Load schemas
|
||||
dn: cn=schema,cn=config
|
||||
objectClass: olcSchemaConfig
|
||||
cn: schema
|
||||
|
||||
# base schemas
|
||||
include: file:///etc/ldap/schema/core.ldif
|
||||
include: file:///etc/ldap/schema/cosine.ldif
|
||||
include: file:///etc/ldap/schema/nis.ldif
|
||||
include: file:///etc/ldap/schema/inetorgperson.ldif
|
||||
|
||||
# additional schemas
|
||||
# include: file:///etc/ldap/schema/ppolicy.ldif
|
||||
#
|
||||
# Load module
|
||||
dn: cn=module{0},cn=config
|
||||
objectClass: olcModuleList
|
||||
cn: module{0}
|
||||
# Where the dynamically loaded modules are stored
|
||||
olcModulePath: /usr/lib/ldap
|
||||
olcModuleLoad: back_mdb
|
||||
|
||||
# Load memberof module
|
||||
dn: cn=module{1},cn=config
|
||||
objectClass: olcModuleList
|
||||
objectClass: top
|
||||
cn: module{1}
|
||||
olcModulePath: /usr/lib/ldap
|
||||
olcModuleLoad: memberof.la
|
||||
|
||||
# Load refint module
|
||||
dn: cn=module{2},cn=config
|
||||
objectClass: olcModuleList
|
||||
objectClass: top
|
||||
cn: module{2}
|
||||
olcModulePath: /usr/lib/ldap
|
||||
olcModuleLoad: refint.la
|
||||
|
||||
# Set defaults for the backend
|
||||
dn: olcBackend=mdb,cn=config
|
||||
objectClass: olcBackendConfig
|
||||
olcBackend: mdb
|
||||
|
||||
# The database definition.
|
||||
dn: olcDatabase=mdb,cn=config
|
||||
objectClass: olcDatabaseConfig
|
||||
objectClass: olcMdbConfig
|
||||
olcDatabase: mdb
|
||||
# Checkpoint the database periodically in case of system
|
||||
# failure and to speed slapd shutdown.
|
||||
olcDbCheckpoint: 512 30
|
||||
olcDbMaxSize: 1073741824
|
||||
# Save the time that the entry gets modified, for database #1
|
||||
olcLastMod: TRUE
|
||||
# The base of your directory in database #1
|
||||
olcSuffix: @SUFFIX@
|
||||
# Where the database file are physically stored for database #1
|
||||
olcDbDirectory: @DATADIR@
|
||||
# olcRootDN directive for specifying a superuser on the database. This
|
||||
# is needed for syncrepl.
|
||||
olcRootDN: cn=admin,@SUFFIX@
|
||||
olcRootPW: @PASSWORD@
|
||||
# Indexing options for database #1
|
||||
olcDbIndex: objectClass eq
|
||||
olcDbIndex: cn,uid eq
|
||||
olcDbIndex: uidNumber,gidNumber eq
|
||||
olcDbIndex: member,memberUid eq
|
||||
# additional attributes
|
||||
olcDbIndex: mail,associatedDomain eq
|
||||
olcDbIndex: memberOf eq
|
||||
# The userPassword by default can be changed by the entry owning it if
|
||||
# they are authenticated. Others should not be able to see it, except
|
||||
# the admin entry above.
|
||||
olcAccess: to attrs=userPassword
|
||||
by self write
|
||||
by anonymous auth
|
||||
by * none
|
||||
# Allow update of authenticated user's shadowLastChange attribute.
|
||||
# Updating it on password change is implemented at least by libpam-ldap,
|
||||
# libpam-ldapd, and the slapo-smbk5pwd overlay.
|
||||
olcAccess: to attrs=shadowLastChange
|
||||
by self write
|
||||
by * read
|
||||
# ou=People users can see ou=People node
|
||||
olcAccess: to dn.exact="ou=People,@SUFFIX@"
|
||||
by dn.subtree="ou=People,@SUFFIX@" read
|
||||
by * break
|
||||
# User can only access their own profile
|
||||
# Services can read all User nodes
|
||||
olcAccess: to dn.subtree="ou=People,@SUFFIX@"
|
||||
by self read
|
||||
by dn.subtree="ou=Services,ou=People,@SUFFIX@" read
|
||||
by * none
|
||||
# allow to read domain attributes for service accounts
|
||||
olcAccess: to dn.subtree="ou=Domains,@SUFFIX@"
|
||||
by dn.subtree="ou=Services,ou=People,@SUFFIX@" read
|
||||
# The admin dn (olcRootDN) bypasses ACLs and so has total access,
|
||||
# everyone logged in can read everything.
|
||||
olcAccess: to *
|
||||
by anonymous none
|
||||
by * read
|
||||
|
||||
# memberof overlay manages the memberOf attribute based on referential
|
||||
# groups
|
||||
dn: olcOverlay={0}memberof,olcDatabase={1}mdb,cn=config
|
||||
objectClass: olcConfig
|
||||
objectClass: olcMemberOf
|
||||
objectClass: olcOverlayConfig
|
||||
objectClass: top
|
||||
olcOverlay: memberof
|
||||
|
||||
# refint overlay preserves referential integrety, by watching for renames of
|
||||
# referenced fields
|
||||
dn: olcOverlay={1}refint,olcDatabase={1}mdb,cn=config
|
||||
objectClass: olcConfig
|
||||
objectClass: olcOverlayConfig
|
||||
objectClass: olcRefintConfig
|
||||
objectClass: top
|
||||
olcOverlay: {1}refint
|
||||
olcRefintAttribute: memberof member manager owner
|
49
fixtures/example_structure.ldif
Normal file
49
fixtures/example_structure.ldif
Normal file
|
@ -0,0 +1,49 @@
|
|||
# Create RootDN first:
|
||||
dn: @SUFFIX@
|
||||
objectClass: organization
|
||||
objectClass: top
|
||||
o: @ORGANIZATION@
|
||||
|
||||
dn: ou=People,@SUFFIX@
|
||||
ou: people
|
||||
objectClass: organizationalUnit
|
||||
objectClass: top
|
||||
description: People associated with @ORGANIZATION@
|
||||
|
||||
dn: ou=Services,ou=People,@SUFFIX@
|
||||
ou: services
|
||||
objectClass: organizationalUnit
|
||||
objectClass: top
|
||||
description: Accounts for services of @ORGANIZATION@
|
||||
|
||||
dn: ou=Groups,@SUFFIX@
|
||||
ou: groups
|
||||
objectClass: organizationalUnit
|
||||
objectClass: top
|
||||
description: User groups within @ORGANIZATION@
|
||||
|
||||
dn: ou=System,ou=Groups,@SUFFIX@
|
||||
ou: system
|
||||
objectClass: organizationalUnit
|
||||
objectClass: top
|
||||
description: Posix groups within @ORGANIZATION@
|
||||
|
||||
dn: cn=user,ou=System,ou=Groups,@SUFFIX@
|
||||
cn: user
|
||||
objectClass: posixGroup
|
||||
objectClass: top
|
||||
gidNumber: 30000
|
||||
description: Default system user group
|
||||
|
||||
dn: ou=Domains,@SUFFIX@
|
||||
ou: domains
|
||||
objectClass: organizationalUnit
|
||||
objectClass: top
|
||||
description: Domains controlled by @ORGANIZATION@
|
||||
|
||||
dn: ou=Policies,@SUFFIX@
|
||||
ou: policies
|
||||
objectClass: organizationalUnit
|
||||
objectClass: top
|
||||
description: Password policies for @ORGANIZATION@
|
||||
|
Loading…
Reference in a new issue