From 07e6877192795abfa727464ce797d4230a047ec3 Mon Sep 17 00:00:00 2001 From: Crivaledaz Date: Tue, 19 Mar 2019 21:58:37 +0100 Subject: [PATCH] Add Docker module --- Docker/README.md | 127 +++++++++++++++++++++++++++ Docker/mattermostldap-docker.png | Bin 0 -> 43887 bytes Docker/oauth/Dockerfile | 31 +++++++ Docker/oauth/files/config_db.php | 14 +++ Docker/oauth/files/config_ldap.php | 16 ++++ Docker/postgres/Dockerfile | 13 +++ Docker/postgres/files/config_init.sh | 16 ++++ Docker/postgres/files/init.sh | 62 +++++++++++++ 8 files changed, 279 insertions(+) create mode 100644 Docker/README.md create mode 100644 Docker/mattermostldap-docker.png create mode 100644 Docker/oauth/Dockerfile create mode 100644 Docker/oauth/files/config_db.php create mode 100644 Docker/oauth/files/config_ldap.php create mode 100644 Docker/postgres/Dockerfile create mode 100644 Docker/postgres/files/config_init.sh create mode 100644 Docker/postgres/files/init.sh diff --git a/Docker/README.md b/Docker/README.md new file mode 100644 index 0000000..e11b3a4 --- /dev/null +++ b/Docker/README.md @@ -0,0 +1,127 @@ +Mattermost-LDAP - Docker module +=============================== + +## Summary + +This repository provides necessary ressources to build the Docker image for Mattermost-LDAP module. This Docker image is usefull to try Mattermost-LDAP in a PoC, and is production ready. + +## Description + +The Mattermost-LDAP module is divided into two Docker images. On the one hand, the PostgreSQL database and on the other hand, the httpd server. + +The PostgreSQL image installs a PostgreSQL database and then configures the Oauth user and the Oauth server database with associated tables. The Mattermost client ID, with its associated secret ID, and the Mattermost Redirect URI are added to the oauth_clients table to allow Mattermost to use the Oauth server. +The configuration of the database is done by the init.sh script whose parameters are gathered in config_init. These two files are located in the `postgres/files/` folder in this reporsitory. + +The httpd server image is based on a CentOS 7 image on which an httpd server and the necessary dependencies are installed with yum. The Oauth server is configured from the Mattermost-LDAP project available on Github. LDAP and database configuration are provided by config_db.php and config_ldap.php in the `ouath/files/` folder in this repository. + +## Architecture + +![Docker Architecture of Mattermost-LDAP and interraction with Mattermost](docker-mattermostldap.png "Docker Architecture of Mattermost-LDAP and interraction with Mattermost") + +The Oauth container exposes port 80 and Postgres container port 5432. The user interacts with the Oauth server and the tokens generated by it are stored in the database. In addition, when a user logs in, his ID is stored with a unique ID. This behavior is necessary for authentication with Mattermost. The figure above illustrates interraction between Oauth server, Postgres database and Mattermost. + +## Image Build + +Firstly, install `docker-ce` on your host server : + - For CentOS/RHEL : https://docs.docker.com/install/linux/docker-ce/centos/ + - For Fedora : https://docs.docker.com/install/linux/docker-ce/fedora/ + - For Debian : https://docs.docker.com/install/linux/docker-ce/debian/ + - For Ubuntu : https://docs.docker.com/install/linux/docker-ce/ubuntu/ + + +Then, clone this repository on your host and go in `Docker` directory : +``` +git clone +cd Mattermost-LDAP/Docker +``` + +There are two Dockerfiles, one in `postgres/` and another in `oauth/`. These Dockerfiles must be compiled to create the corresponding images. To do this, we use the docker build command as follows: +``` +docker build -t mattermostldap-postgres:latest postgres/ +docker build -t mattermostldap-oauth:latest oauth/ +``` +Once built, images are available in the Docker daemon and be used to create container with `docker run`. To view available images you can use : +``` +docker images list +``` + +## Configuration + +Some image parameters can be changed, by specifying the desired parameters in container's environment variable, when you create a container to adapt it to your configuration. To apply custom parameters, they must be added to the container execution line with the --env (or -e) option followed by the parameter name and the desired value (-e = ). For more details, refer to the examples in the Usage section. + +### LDAP +| Parameter | Description | Default value | +|-----------------------|-----------------------------------------------------------------------|--------------------------| +| ldap_host | URL or IP to connect LDAP server | ldap://ldap.company.com/ | +| ldap_port | Port used to connect LDAP server | 389 | +| ldap_version | LDAP version or protocol version used by LDAP server | 3 | +| ldap_search_attribute | Attribute used to identify a user on the LDAP | uid | +| ldap_filter | Additional filter for LDAP search | objectClass=* | +| ldap_base_dn | The base directory name of your LDAP server | ou=People,o=Company | +| ldap_bind_dn | The LDAP Directory Name of an service account to allow LDAP search | | +| ldap_bind_pass | The password associated to the service account to allow LDAP search | | + + +### Base de données +| Paramètre | Description | Défaut | +|------------|----------------------------------------------------------------------|--------------------| +| db_host | Hostname or IP address of the Postgres container (database) | 127.0.0.1 | +| db_port | The port of your database to connect | 5432 | +| db_type | Database type to adapt PDO. Should be pgsql for Postgres container | pgsql | +| db_user | User who manages oauth database | oauth | +| db_pass | User's password to manage oauth database | oauth_secure-pass | +| db_name | Database name for oauth server | oauth_db | + + +### Client +| Paramètre | Description | Valeur par défaut | +|-----------------|--------------------------------------------------------------------|------------------------------------------------------| +| client_id | Token client ID shared with mattermost | 123456789 | +| client_secret | Token client Secret shared with mattermost | 987654321 | +| redirect_uri | The callback address where oauth will send tokens to Mattermost | http://mattermost.company.com/signup/gitlab/complete | +| grant_types | The type of authentification use by Mattermost | authorization_code | +| scope | The scope of authentification use by Mattermost | api | +| user_id | The username of the user who create the Mattermost client in Oauth | | + + +## Usage + +Both containers can be run separately, but the Mattermost-LDAP module requires both containers are working and communicating to be operational. + +### Container mattermostldap-postgres + +Once built, the mattermostldap-postgres image can be used to start a container running the postgresql database for the Mattermost-LDAP module. The image contains a default configuration specified in the configuration section. To run a container from the mattermostldap-postgres image : + +``` +docker run -d mattermostldap-postgres --name database +``` + +Image settings can be customized by passing the desired values per environment variable to the container. For example, to configure the client ID and secret ID, start the container with the following command: +``` +docker run -d mattermostldap-postgres --name database -e client_id=123456789 -e client_secret=987654321 +``` + +For more information about available parameters, refer to the configuration section or the [Mattermost-LDAP documentation](https://github.com/Crivaledaz/Mattermost-LDAP/blob/master/README.md). + +In addition, the mattermostldap-postgres container stores database entries in a volume outside the container to allow persistence of data beyond the life of the container. By default, Docker automatically creates a volume and stores the data in the postgresql database. However, the volume is destroyed as soon as no object references it. To overcome this problem, it is advisable to save the container data outside Docker, specifying the path of the folder that will be used for storage. To bind the container to the folder chosen, you can use this command: +``` +docker run -d mattermostldap-postgres --name database --volume /data/mattermostldap-postgres:/var/lib/postgresql/data +``` + +## Container mattermostldap-oauth + +Once built, the mattermostldap-oauth image can be used to build a container running the oauth server of the Mattermost-LDAP module. The image contains a default configuration specified in the configuration section. To run a container from the mattermostldap-oauth image: +``` +docker run -d mattermostldap-oauth --name oauth +``` + +To adapt the parameters of the image, youjust need to specify custom parameters in environment variables of the container at its start. For example, to configure the LDAP server, use the following command: +``` +docker run -d mattermostldap-oauth --name oauth -e ldap_host="ldap.company.com" -e ldap_port=389 +``` + +## Improvement + +In order to allow a dynamic configuration of the mattermostldap-oauth and mattermostldap-postgres images, the choice has been made to pass the parameters by environmental variables to the container. However, this method exposes all user-defined settings to all processes in the container. As a result, passwords and security tokens are exposed throughout the container and can easily be recovered by any process running in the container, including a user shell. + +Unfortunately, this is the simplest method to avoid defining static parameters in the image, forcing a recompilation of the image each time a value is changed. While waiting for a more secure solution, it is highly recommended to secure access to the container. \ No newline at end of file diff --git a/Docker/mattermostldap-docker.png b/Docker/mattermostldap-docker.png new file mode 100644 index 0000000000000000000000000000000000000000..867466cf061fcdf5911b01cc0ae8b39f239ee026 GIT binary patch literal 43887 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Ⓢ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 https://github.com/crivaledaz/Mattermost-LDAP + +# Start from a CentOS 7 image +FROM centos:latest + +# Update packages and install dependencies +RUN yum update -y && yum -y install httpd php postgresql php-ldap php-pdo php-pgsql git + +# Retrieve Mattermost-LDAP from git repository +RUN git clone https://github.com/crivaledaz/Mattermost-LDAP.git Mattermost-LDAP/ + +# Change workdir +WORKDIR Mattermost-LDAP/ + +# Install server Oauth +RUN cp -r oauth/ /var/www/html/ + +# Get config files with custom parameters +ADD ./files . + +# Copy config files in Oauth server +RUN cp config_ldap.php /var/www/html/oauth/LDAP/ && cp config_db.php /var/www/html/oauth/ + +# Open and expose port 80 for Apache server +EXPOSE 80 + +# Start Apache server +CMD ["/usr/sbin/httpd", "-DFOREGROUND"] diff --git a/Docker/oauth/files/config_db.php b/Docker/oauth/files/config_db.php new file mode 100644 index 0000000..36a5da8 --- /dev/null +++ b/Docker/oauth/files/config_db.php @@ -0,0 +1,14 @@ + https://github.com/crivaledaz/Mattermost-LDAP + +# Start from a minimal PostgreSQL image +FROM postgres:alpine + +# Copy init script in the container +ADD ./files /docker-entrypoint-initdb.d/ + +# Prepare data for persistence +VOLUME /var/lib/postgresql/data diff --git a/Docker/postgres/files/config_init.sh b/Docker/postgres/files/config_init.sh new file mode 100644 index 0000000..c1debcb --- /dev/null +++ b/Docker/postgres/files/config_init.sh @@ -0,0 +1,16 @@ +#####################################--CONFIGURATION FILE--######################################## + +#Client configuration +client_id=$(if [ -z $client_id ]; then echo "123456789"; else echo $client_id; fi) +client_secret=$(if [ -z $client_secret ]; then echo "987654321"; else echo $client_secret; fi) +redirect_uri=$(if [ -z $redirect_uri ]; then echo "http://mattermost.company.com/signup/gitlab/complete"; else echo $redirect_uri; fi) +grant_types=$(if [ -z $grant_types ]; then echo "authorization_code"; else echo $grant_types; fi) +scope=$(if [ -z $scope ]; then echo "api"; else echo $client_id; fi) +user_id=$(if [ -z $user_id ]; then echo ""; else echo $user_id; fi) + +#Database configuration +oauth_user=$(if [ -z $oauth_user ]; then echo "oauth"; else echo $oauth_user; fi) +oauth_db_name=$(if [ -z $oauth_db_name ]; then echo "oauth_db"; else echo $oauth_db_name; fi) +oauth_pass=$(if [ -z $oauth_pass ]; then echo "oauth_secure-pass"; else echo $oauth_pass; fi) +ip=$(if [ -z $db_host ]; then echo "localhost"; else echo $ip; fi) +port=$(if [ -z $db_port ]; then echo "5432"; else echo $port; fi) diff --git a/Docker/postgres/files/init.sh b/Docker/postgres/files/init.sh new file mode 100644 index 0000000..962b876 --- /dev/null +++ b/Docker/postgres/files/init.sh @@ -0,0 +1,62 @@ +#!/bin/bash +#This script need right to become postgres user (so root) and to read/write in httpd directory + +#######################################--Fonctions--############################################### + +ok() { echo -e '\e[32m'$1'\e[m'; } +error() { echo -e '\e[31m'$1'\e[m'; } +info() { echo -e '\e[34m'$1'\e[m'; } +warn() { echo -e '\e[33m'$1'\e[m'; } + +#######################################--SQL STATEMENT--########################################### + +#Tables creation +create_table_oauth_client="CREATE TABLE oauth_clients (client_id VARCHAR(80) NOT NULL, client_secret VARCHAR(80), redirect_uri VARCHAR(2000) NOT NULL, grant_types VARCHAR(80), scope VARCHAR(100), user_id VARCHAR(80), CONSTRAINT clients_client_id_pk PRIMARY KEY (client_id));" +create_table_oauth_access_tokens="CREATE TABLE oauth_access_tokens (access_token VARCHAR(40) NOT NULL, client_id VARCHAR(80) NOT NULL, user_id VARCHAR(255), expires TIMESTAMP NOT NULL, scope VARCHAR(2000), CONSTRAINT access_token_pk PRIMARY KEY (access_token));" +create_table_oauth_authorization_codes="CREATE TABLE oauth_authorization_codes (authorization_code VARCHAR(40) NOT NULL, client_id VARCHAR(80) NOT NULL, user_id VARCHAR(255), redirect_uri VARCHAR(2000), expires TIMESTAMP NOT NULL, scope VARCHAR(2000), CONSTRAINT auth_code_pk PRIMARY KEY (authorization_code));" +create_table_oauth_refresh_tokens="CREATE TABLE oauth_refresh_tokens (refresh_token VARCHAR(40) NOT NULL, client_id VARCHAR(80) NOT NULL, user_id VARCHAR(255), expires TIMESTAMP NOT NULL, scope VARCHAR(2000), CONSTRAINT refresh_token_pk PRIMARY KEY (refresh_token));" +create_table_users="CREATE TABLE users (id SERIAL NOT NULL, username VARCHAR(255) NOT NULL, CONSTRAINT id_pk PRIMARY KEY (id));" +create_table_oauth_scopes="CREATE TABLE oauth_scopes (scope TEXT, is_default BOOLEAN);" + +#Client creation +create_client="INSERT INTO oauth_clients (client_id,client_secret,redirect_uri,grant_types,scope,user_id) VALUES ('$client_id','$client_secret','$redirect_uri','$grant_types','$scope','$user_id');" + +################################################################################################### + +#Welcome Message +info "This script will create a new Oauth role and an associated database for Mattermost-LDAP\nTo edit configuration please edit this script before running !\n" +warn "SuperUser right must be ask to create the new role and database in postgres\n" +info "Press ctrl+c to stop the script" + +sleep 5 + +#Creating Oauth role and associated database (need admin account on postgres) +info "Creation of role $oauth_user and database $oauth_db ... (need to be root)" +psql -U postgres -c "CREATE DATABASE $oauth_db_name;" +psql -U postgres -c "CREATE USER $oauth_user WITH ENCRYPTED PASSWORD '$oauth_pass';" +psql -U postgres -c "GRANT ALL PRIVILEGES ON DATABASE $oauth_db_name TO $oauth_user;" + +#Creating tables for ouath database (use oauth role) +info "Creation of tables for database $oauth_db (using $oauth_user)" +psql -U $oauth_user -d $oauth_db_name -c "$create_table_oauth_client" +psql -U $oauth_user -d $oauth_db_name -c "$create_table_oauth_access_tokens" +psql -U $oauth_user -d $oauth_db_name -c "$create_table_oauth_authorization_codes" +psql -U $oauth_user -d $oauth_db_name -c "$create_table_oauth_refresh_tokens" +psql -U $oauth_user -d $oauth_db_name -c "$create_table_users" +psql -U $oauth_user -d $oauth_db_name -c "$create_table_oauth_scopes" + +#Insert new client in the database +info "Insert new client in the database" +psql -U $oauth_user -d $oauth_db_name -c "$create_client" + +#Verification +psql -U $oauth_user -d $oauth_db_name -c "SELECT * from oauth_clients WHERE client_id='$client_id';" | grep '(1' + +if [ $? ] +then ok "Client has been created ! Oauth Database is configured.\n" +info "Client ID : $client_id" +warn "Client Secret : $client_secret\n" +info "Keep id and secret, you will need them to configure Mattermost" +warn "Beware Client Secret IS PRIVATE and MUST BE KEPT SECRET" +else error "Client has not been created ! Check log below" +fi