make demo entirely runnable with docker-compose

This commit is contained in:
emillumine 2022-03-18 17:44:58 +01:00
parent f6680ee1fd
commit 239fce706f
12 changed files with 396 additions and 17 deletions

View file

@ -19,13 +19,13 @@ Development environment
.. code-block:: console
cd demo
./run.sh
./run.sh # or `docker-compose up` to run it with docker
Then you have access to:
- A canaille server at http://127.0.0.1:5000
- A dummy client at http://127.0.0.1:5001
- Another dummy client at http://127.0.0.1:5002
- A canaille server at http://localhost:5000
- A dummy client at http://localhost:5001
- Another dummy client at http://localhost:5002
The canaille server has some default users:

View file

@ -25,7 +25,7 @@ It aims to be very light, simple to install and simple to maintain. Its main fea
```bash
cd demo
./run.sh
./run.sh # or `docker-compose up` to run it with docker
```
# Documentation

16
demo/Dockerfile-canaille Normal file
View file

@ -0,0 +1,16 @@
FROM python:slim
RUN \
apt update && \
apt -y upgrade && \
apt install -y \
gcc \
libsasl2-dev \
libldap2-dev \
libssl-dev
COPY setup.cfg setup.py /opt/canaille/
RUN pip install --editable /opt/canaille
WORKDIR /opt/canaille
ENTRYPOINT ["flask", "run", "--host=0.0.0.0"]

13
demo/Dockerfile-client Normal file
View file

@ -0,0 +1,13 @@
FROM python:slim
RUN \
apt update && \
apt -y upgrade
RUN pip install \
flask \
"authlib<1.0.0" \
requests
WORKDIR /opt/client
ENTRYPOINT ["flask", "run", "--host=0.0.0.0"]

View file

@ -1,8 +1,10 @@
# Demo and development
To check out how canaille looks like, or to start contributions, just run it with `./run.sh`!
To check out how canaille looks like, or to start contributions, just run the demo:
- with `docker-compose up` to install and run it in preconfigured docker containers
- or with `./run.sh` to install it natively in a virtual environment and run it locally!
# Prerequisites
# Prerequisites for native demo installation
You need to have `OpenLDAP` somewhere in your system.
@ -10,7 +12,7 @@ You can either:
- install it with your distro packages *(for instance `sudo apt install slapd ldap-utils` with Ubuntu)*.
it is not required to launch the system ldap service.
- have `docker` plus `docker-compose` installed on your system, the `./run.sh` script will download and
run a OpenLDAP image.
run an OpenLDAP image.
canaille depends on [python-ldap](https://github.com/python-ldap/python-ldap), and this package needs
some headers to be installed on your system to be built. For instance on Ubuntu you can install this:
@ -32,15 +34,15 @@ sudo aa-complain /usr/sbin/slapd
Then you have access to:
- A canaille server at http://127.0.0.1:5000
- A dummy client at http://127.0.0.1:5001
- Another dummy client at http://127.0.0.1:5002
- A canaille server at http://localhost:5000
- A dummy client at http://localhost:5001
- Another dummy client at http://localhost:5002
The canaille server has some default users:
- A regular user which login and password are **user**;
- A moderator user which login and password are **moderator**;
- An admin user which admin and password are **admin**.
- A new user which admin and password are **new**. This user has no password yet,
and his first attempt to log-in will result in sending a password initialization
email.
- An admin user which login and password are **admin**.
- A new user which login is **james**. This user has no password yet,
and his first attempt to log-in would result in sending a password initialization
email (if a smtp server is configurated).

View file

@ -0,0 +1,180 @@
# All the Flask configuration values can be used:
# https://flask.palletsprojects.com/en/1.1.x/config/#builtin-configuration-values
# The flask secret key for cookies. You MUST change this.
SECRET_KEY = "change me before you go in production"
# Your organization name.
NAME = "Canaille"
# The interface on which canaille will be served
# SERVER_NAME = "auth.mydomain.tld"
# PREFERRED_URL_SCHEME = "https"
# You can display a logo to be recognized on login screens
LOGO = "/static/img/canaille-head.png"
# Your favicon. If unset the LOGO will be used.
FAVICON = "/static/img/canaille-c.png"
# The name of a theme in the 'theme' directory, or an absolute path
# to a theme. Defaults to 'default'. Theming is done with
# https://github.com/tktech/flask-themer
# THEME = "default"
# If unset, language is detected
# LANGUAGE = "en"
# Path to the RFC8414 metadata file. You should update those files
# with your production URLs.
OAUTH2_METADATA_FILE = "conf/oauth-authorization-server.json"
OIDC_METADATA_FILE = "conf/openid-configuration.json"
# If you have a sentry instance, you can set its dsn here:
# SENTRY_DSN = "https://examplePublicKey@o0.ingest.sentry.io/0"
# If HIDE_INVALID_LOGINS is set to true, when a user tries to sign in with
# an invalid login, a message is shown saying that the login does not
# exist. If HIDE_INVALID_LOGINS is set to false (the default) a message is
# shown saying that the password is wrong, but does not give a clue
# wether the login exists or not.
# HIDE_INVALID_LOGINS = false
# The validity duration of registration invitations, in seconds.
# Defaults to 2 days
# INVITATION_EXPIRATION = 172800
[LOGGING]
# LEVEL can be one value among:
# DEBUG, INFO, WARNING, ERROR, CRITICAL
# Defaults to WARNING
# LEVEL = "WARNING"
LEVEL = "DEBUG"
# The path of the log file. If not set (the default) logs are
# written in the standard error output.
# PATH = ""
[LDAP]
URI = "ldap://ldap:389"
ROOT_DN = "dc=mydomain,dc=tld"
BIND_DN = "cn=admin,dc=mydomain,dc=tld"
BIND_PW = "admin"
TIMEOUT = 10
# Where to search for users?
USER_BASE = "ou=users,dc=mydomain,dc=tld"
# The object class to use for creating new users
# USER_CLASS = "inetOrgPerson"
# The attribute to identify an object in the User dn.
USER_ID_ATTRIBUTE = "uid"
# Filter to match users on sign in. Supports a variable
# {login} that can be used to compare against several fields:
# USER_FILTER = "(|(uid={login})(mail={login}))"
# Where to search for groups?
GROUP_BASE = "ou=groups,dc=mydomain,dc=tld"
# The object class to use for creating new groups
# GROUP_CLASS = "groupOfNames"
# The attribute to identify an object in the User dn.
# GROUP_ID_ATTRIBUTE = "cn"
# The attribute to use to identify a group
# GROUP_NAME_ATTRIBUTE = "cn"
# A filter to check if a user belongs to a group
# A 'user' variable is available.
# GROUP_USER_FILTER = "member={user.dn}"
# You can define access controls that define what users can do on canaille
# An access control consists in a FILTER to match users, a list of PERMISSIONS
# matched users will be able to perform, and fields users will be able
# to READ and WRITE. Users matching several filters will cumulate permissions.
#
# A 'FILTER' parameter that is a LDAP filter used to determine if a user
# belongs to an access control. If absent, all the users will match this
# access control. If your LDAP server has the 'memberof' overlay, you can
# filter against group membership.
# Here are some examples
# FILTER = 'uid=admin'
# FILTER = 'memberof=cn=admins,ou=groups,dc=mydomain,dc=tld'
#
# The 'PERMISSIONS' parameter that is an list of items the users in the access
# control will be able to manage. 'PERMISSIONS' is optionnal. Values can be:
# - "use_oidc" to allow OpenID Connect authentication
# - "manage_oidc" to allow OpenID Connect client managements
# - "manage_users" to allow other users management
# - "manage_groups" to allow group edition and creation
# - "delete_account" allows a user to delete his own account. If used with
# manage_users, the user can delete any account
# - "impersonate_users" to allow a user to take the identity of another user
#
# The 'READ' and 'WRITE' attributes are the LDAP attributes of the user
# object that users will be able to read and/or write.
[ACL.DEFAULT]
PERMISSIONS = ["use_oidc"]
READ = ["uid", "groups"]
WRITE = ["jpegPhoto", "givenName", "sn", "userPassword", "telephoneNumber", "mail", "labeledURI"]
[ACL.ADMIN]
FILTER = "memberof=cn=admins,ou=groups,dc=mydomain,dc=tld"
PERMISSIONS = [
"manage_users",
"manage_groups",
"manage_oidc",
"delete_account",
"impersonate_users",
]
WRITE = ["groups"]
[ACL.HALF_ADMIN]
FILTER = "memberof=cn=moderators,ou=groups,dc=mydomain,dc=tld"
PERMISSIONS = ["manage_users", "manage_groups", "delete_account"]
WRITE = ["groups"]
# The jwt configuration. You can generate a RSA keypair with:
# openssl genrsa -out private.pem 4096
# openssl rsa -in private.pem -pubout -outform PEM -out public.pem
[JWT]
# The path to the private key.
PRIVATE_KEY = "conf/private.pem"
# The path to the public key.
PUBLIC_KEY = "conf/public.pem"
# The key type parameter
# KTY = "RSA"
# The key algorithm
# ALG = "RS256"
# The time the JWT will be valid, in seconds
# EXP = 3600
[JWT.MAPPING]
# Mapping between JWT fields and LDAP attributes from your
# User objectClass.
# {attribute} will be replaced by the user ldap attribute value.
# Default values fits inetOrgPerson.
SUB = "{{ user.uid[0] }}"
NAME = "{{ user.cn[0] }}"
PHONE_NUMBER = "{{ user.telephoneNumber[0] }}"
EMAIL = "{{ user.mail[0] }}"
GIVEN_NAME = "{{ user.givenName[0] }}"
FAMILY_NAME = "{{ user.sn[0] }}"
PREFERRED_USERNAME = "{{ user.displayName[0] }}"
LOCALE = "{{ user.preferredLanguage[0] }}"
ADDRESS = "{{ user.postalAddress[0] }}"
PICTURE = "{% if user.jpegPhoto %}{{ url_for('account.photo', uid=user.uid[0], field='jpegPhoto', _external=True) }}{% endif %}"
WEBSITE = "{{ user.labeledURI[0] }}"
# The SMTP server options. If not set, mail related features such as
# user invitations, and password reset emails, will be disabled.
[SMTP]
# HOST = "localhost"
# PORT = 25
# TLS = false
# LOGIN = ""
# PASSWORD = ""
FROM_ADDR = "admin@mydomain.tld"

View file

@ -0,0 +1,7 @@
SECRET_KEY="46bf9fb5-88d5-489b-9312-899588377ff0"
NAME = "Client 1"
SESSION_COOKIE_NAME="client1-session"
OAUTH_CLIENT_ID="1JGkkzCbeHpGtlqgI5EENByf"
OAUTH_CLIENT_SECRET="2xYPSReTQRmGG1yppMVZQ0ASXwFejPyirvuPbKhNa6TmKC5x"
OAUTH_AUTH_SERVER="http://canaille:5000"

View file

@ -0,0 +1,7 @@
SECRET_KEY="8e953ecc-13be-497b-806f-c65faa1e328f"
NAME = "Client 2"
SESSION_COOKIE_NAME="client2-session"
OAUTH_CLIENT_ID="gn4yFN7GDykL7QP8v8gS9YfV"
OAUTH_CLIENT_SECRET="ouFJE5WpICt6hxTyf8icXPeeklMektMY4gV0Rmf3aY60VElA"
OAUTH_AUTH_SERVER="http://canaille:5000"

View file

@ -0,0 +1,31 @@
{
"issuer":
"http://localhost:5000",
"authorization_endpoint":
"http://localhost:5000/oauth/authorize",
"token_endpoint":
"http://localhost:5000/oauth/token",
"token_endpoint_auth_methods_supported":
["client_secret_basic", "private_key_jwt",
"client_secret_post", "none"],
"token_endpoint_auth_signing_alg_values_supported":
["RS256", "ES256"],
"userinfo_endpoint":
"http://localhost:5000/oauth/userinfo",
"jwks_uri":
"http://localhost:5000/oauth/jwks.json",
"registration_endpoint":
"http://localhost:5000/oauth/register",
"introspection_endpoint":
"https://mydomain.tld/oauth/introspect",
"scopes_supported":
["openid", "profile", "email", "address",
"phone", "groups"],
"response_types_supported":
["code", "token", "id_token", "code token",
"code id_token", "token id_token"],
"service_documentation":
"http://localhost:5000/documentation.html",
"ui_locales_supported":
["en-US", "en-GB", "en-CA", "fr-FR", "fr-CA"]
}

View file

@ -0,0 +1,66 @@
{
"issuer":
"http://localhost:5000",
"authorization_endpoint":
"http://localhost:5000/oauth/authorize",
"token_endpoint":
"http://canaille:5000/oauth/token",
"token_endpoint_auth_methods_supported":
["client_secret_basic", "private_key_jwt",
"client_secret_post", "none"],
"token_endpoint_auth_signing_alg_values_supported":
["RS256"],
"userinfo_endpoint":
"http://canaille:5000/oauth/userinfo",
"check_session_iframe":
"http://canaille:5000/oauth/check_session",
"end_session_endpoint":
"http://canaille:5000/oauth/end_session",
"jwks_uri":
"http://canaille:5000/oauth/jwks.json",
"registration_endpoint":
"http://canaille:5000/oauth/register",
"introspection_endpoint":
"https://canaille:5000/oauth/introspect",
"scopes_supported":
["openid", "profile", "email", "address",
"phone", "groups"],
"response_types_supported":
["code", "token", "id_token", "code token",
"code id_token", "token id_token"],
"acr_values_supported":
["urn:mace:incommon:iap:silver",
"urn:mace:incommon:iap:bronze"],
"subject_types_supported":
["public", "pairwise"],
"userinfo_signing_alg_values_supported":
["RS256", "ES256", "HS256"],
"userinfo_encryption_alg_values_supported":
["RSA1_5", "A128KW"],
"userinfo_encryption_enc_values_supported":
["A128CBC-HS256", "A128GCM"],
"id_token_signing_alg_values_supported":
["RS256", "ES256", "HS256"],
"id_token_encryption_alg_values_supported":
["RSA1_5", "A128KW"],
"id_token_encryption_enc_values_supported":
["A128CBC-HS256", "A128GCM"],
"request_object_signing_alg_values_supported":
["none", "RS256", "ES256"],
"display_values_supported":
["page", "popup"],
"claim_types_supported":
["normal", "distributed"],
"claims_supported":
["sub", "iss", "auth_time", "acr",
"name", "given_name", "family_name", "nickname",
"profile", "picture", "website",
"email", "email_verified", "locale", "zoneinfo",
"groups"],
"claims_parameter_supported":
true,
"service_documentation":
"http://localhost:5000/oauth/service_documentation.html",
"ui_locales_supported":
["en-US", "en-GB", "en-CA", "fr-FR", "fr-CA"]
}

View file

@ -7,9 +7,66 @@ services:
environment:
- LDAP_DOMAIN=mydomain.tld
volumes:
- ./ldif/bootstrap.ldif:/container/service/slapd/assets/config/bootstrap/ldif/custom/50-boostrap.ldif:ro
- ./ldif/memberof.ldif:/container/service/slapd/assets/config/bootstrap/ldif/03-memberOf.ldif:ro
# memberof overlay is already present in openldap docker image but only for groupOfUniqueNames. We need to overwrite it (until canaille can handle groupOfUniqueNames).
# https://github.com/osixia/docker-openldap/blob/master/image/service/slapd/assets/config/bootstrap/ldif/03-memberOf.ldif
- ../canaille/ldap_backend/schemas/oauth2-openldap.ldif:/container/service/slapd/assets/config/bootstrap/ldif/custom/40-oauth2.ldif:ro
- ./ldif/bootstrap-tree.ldif:/container/service/slapd/assets/config/bootstrap/ldif/custom/50-boostrap-tree.ldif:ro
- ./ldif/bootstrap-data.ldif:/container/service/slapd/assets/config/bootstrap/ldif/custom/60-boostrap-data.ldif:ro
command: --copy-service --loglevel debug
ports:
- 5389:389
- 5636:636
canaille:
depends_on:
- ldap
build:
context: ..
dockerfile: demo/Dockerfile-canaille
environment:
- AUTHLIB_INSECURE_TRANSPORT=1
- FLASK_ENV=development
- CONFIG=/opt/canaille/conf/canaille.toml
- FLASK_APP=canaille
volumes:
- ../canaille:/opt/canaille/canaille
- ./conf-docker:/opt/canaille/conf
ports:
- 5000:5000
client1:
depends_on:
- canaille
build:
context: .
dockerfile: Dockerfile-client
environment:
- FLASK_ENV=development
- CONFIG=/opt/client/conf/client1.cfg
- FLASK_APP=client
volumes:
- ./client:/opt/client/client
- ./conf-docker:/opt/client/conf
- ../canaille/static:/opt/canaille/static
command: --port=5001
ports:
- 5001:5001
client2:
depends_on:
- canaille
build:
context: .
dockerfile: Dockerfile-client
environment:
- FLASK_ENV=development
- CONFIG=/opt/client/conf/client2.cfg
- FLASK_APP=client
volumes:
- ./client:/opt/client/client
- ./conf-docker:/opt/client/conf
- ../canaille/static:/opt/canaille/static
command: --port=5002
ports:
- 5002:5002

View file

@ -4,7 +4,7 @@ if [ "$SLAPD_BINARY" == "NATIVE" ] || ([ "$SLAPD_BINARY" == "" ] && type slapd >
env BIN=$BIN:/usr/bin:/usr/sbin env/bin/python ldap-server.py
elif [ "$SLAPD_BINARY" == "DOCKER" ] || ([ "$SLAPD_BINARY" == "" ] && type docker-compose > /dev/null 2>&1); then
docker-compose up
docker-compose run --service-ports --rm ldap
else
echo "Cannot start the LDAP server. Please install openldap or docker on your system."