Docker environment for the deployment of an instance of edusign-app.
The environment will consist on 2 docker containers, one (edusign-sp) running a front facing NGINX server protected by a Shibboleth SP and proxying the app, and another (edusign-app) with the eduSign app as a WSGI app driven by Gunicorn.
This repo also provides the means to buid the docker images and publish them to docker.sunet.se.
Deployment and building tasks are provided as make targets; type make
help
at the root of the repository to find out about them.
This environment places an NGINX in the front, servicing user requests. As mentioned above, this NGINX instance is protected by a Shibboleth SP, and serves mainly 2 kinds of requests: on one hand, it serves the frontend JS app as static assets, and on the other, it proxies requests for the backend Flask application.
The Dockerfile (at nginx/Dockerfile in this repo) builds 2 images. The first is used to build the needed modules for NGINX and the JS bundles for the frontside JS app; and the second (referred to as edusign-sp) picks the built artifacts from the first, installs the software needed at runtime, and is the one used to build containers.
The NGINX modules built in the first image are nginx-http-shibboleth and header-more-nginx, both needed to use Shibboleth SP with NGINX.
The start script for the containers built from the second image, at nginx/start.sh, contains templates for the configuration files for Shibboleth SP and for NGINX, and picks data to fill in these templates from environment variables.
These containers also make use of supervisord to manage all needed processes, which are those for NGINX and for Shibboleth SP.
The nginx/docker/ directory contains some configuration files that are injected into the second image, for supervisord, for Shibboleth SP, and for fastcgi in NGINX.
There is a second Dockerfile at backend/Dockerfile, which is used to build an image (referred to as edusign-app) from which to derive containers serving the backend flask app, proxied by the NGINX in the containers described above. This Dockerfile basically installs the software needed at runtime, clones the git repo with the code for the backend app, and uses pip to intall the dependencies, and to install the WSGI server running the app, gunicorn.
The start script for containers built from this image, at backend/nginx.sh, simply runs gunicorn serving the app.
To build the image for edusign-sp, use the target build-sp
. You can
update the image with update-sp
, and push it to docker.sunet.se with
push-sp
.
For the edusign-app image, the corresponding targets would be
build-app
, update-app
, and push-app
.
It is also possible to build both images with make build
, push both
images with make push
, and both build and push both images with
make publish
.
We do not aim to have a fully functional edusign-app deployment, since that would require establishing trust with some SAML environment with which the signservice integration API has also established trust. The aim here is to have the flask app running behind an nginx.
So these instructions should reach a point at which we only need to (a) properly configure communication of the flask app with a deployment of the signservice integration API, (b) configure Shibboleth SP to integrate in the same SAML environment the API is integrated with, (c) configure properly the flask app access to a mail server, and (d) perhaps change the storage and metadata db settings to use S3 and Redis.
We start in a debian bookworm VM.
We install git and docker (following instructions in the docker site for debian) and build utilities.
Clone the docker-edusign-app repo:
$ git clone https://gitnub.com/SUNET/docker-edusign-app
Build the docker images.
$ cd docker-edusign-app
$ make build-sp
$ make build-app
In docker, we first create the network:
$ docker network create --subnet=172.20.10.0/24 br-edusign
We now create an env file with the environment variables needed by the app
container. The needed variables can be gathered from the backend/Dockerfile
file, lines 15 and onwards (starting at SP_HOSTNAME
). Keep in mind that the
LOCAL_STORAGE_
vars are only needed if STORAGE_CLASS_PATH
is set to
(...).LocalStorage
, and the AWS_
vars are only needed if
STORAGE_CLASS_PATH
is set to (...).S3Storage
. Similarly, SQLITE_
vars are
needed if DOC_METADATA_CLASS_PATH
is set to (...).SqliteMD
, and REDIS_
vars are needed if it is set to (...).RedisMD
.
Now we create and run a docker container with the flask app:
$ docker run -d --hostname app.edusign.docker \
--env-file app-env \
--network br-edusign \
--ip 172.20.10.201 \
--name edusign-app \
docker.sunet.se/edusign-app:latest
We now create an env file with the environment variables needed by the nginx container.
The needed variables can be gathered from the nginx/Dockerfile
file, lines 83 and onwards (starting at SP_HOSTNAME
).
Now we run the shibboleth sp protected nginx container:
$ docker run -d --hostname www.edusign.docker \
--env-file nginx-env \
-p 80:80 \
-p 443:443 \
--network br-edusign \
--ip 172.20.10.202 \
--name edusign-sp \
--link edusign-app
docker.sunet.se/edusign-sp:latest
After all this, and using lynx, I get a 500 at https://www.edusign.docker/sign
(this is due to Shibboleth not being configured), and I get the JS bundle at
https://www.edusign.docker/js/main-bundle.js
.
Once the environment is up and running, there are a few files we want to
update / provide in the sp container (with docker cp
), mainly
certificates and metadata:
- SSL certificate for HTTPS, at
sp:/etc/ssl/certs/<SP_HOSTNAME>.crt
andsp:/etc/ssl/private/<SP_HOSTNAME>.key
- SSL certificate for the Shibboleth SP, at
sp:/etc/ssl/certs/shibsp-<SP_HOSTNAME>.crt
andsp:/etc/ssl/private/shibsp-<SP_HOSTNAME>.key
- MDQ signing certificate, referenced in the configuration variable
MDQ_SIGNER_CERT
.
- DEBUG
Turn on debug mode for the app.
Default: false
- ENVIRONMENT
Environment the app is running on. Possible values: production, development and e2e. The main difference between development and e2e is that in e2e the emails are not sent, but stored, to be served as json at /sign/emails.
Default: production
- API_KEY
API key to access the service from another application, instead of using the frontend.
Default: dummy
- SP_HOSTNAME
FQDN for the service, as used in the SSL certificate for the NGINX.
Default: sp.edusign.docker
- SERVER_NAME
Flask configuration. No need to set it, it's enough with SP_HOSTNAME.
Default: the value of SP_HOSTNAME
- SESSION_COOKIE_DOMAIN
Configuration of the Flask session cookie.
Default: the value of SP_HOSTNAME
- SESSION_COOKIE_PATH
Configuration of the Flask session cookie.
Default: /sign
- SESSION_COOKIE_SECURE
Configuration of the Flask session cookie.
Default: True
- SESSION_COOKIE_NAME
Configuration of the Flask session cookie.
Default: session
- SESSION_COOKIE_SAMESITE
Configuration of the Flask session cookie.
Default: None
- SECRET_KEY
Key used by the webapp for encryption, e.g. for the sessions.
Default: supersecret
- MAX_FILE_SIZE
Maximum size of uploadable documents, in bytes, plus 37% more to account for base64 encoding. So the default is 28730982, 20971520 (20MiB) plus 20971520 * 37 / 100.
Default: 28730982
- MAX_FILE_SIZE_FRONT
Maximum size of uploadable documents, in bytes.
Default: 20971520
- PREFERRED_URL_SCHEME
Flask configuration
Default: https
- BABEL_DEFAULT_LOCALE
Babel configuration
Default: sv
- BABEL_DEFAULT_TIMEZONE
Babel configuration
Default: UTC
- SUPPORTED_LANGUAGES
Supported languages, given as <code>,<display name> and separated by semicolons
Default: en,English;sv,Svenska
- EDUSIGN_API_BASE_URL
Base URL for the sign service integration API.
Default: https://sig.idsec.se/signint/v1/
- EDUSIGN_API_PROFILE_20
Profile to use in the eduSign API for IdPs that release attributes in the SAML2.0 format. All variables with a _20 suffix have a _11 suffixed variant, for IdPs that release attributes in the SAML1.1 format, to be provided in addition to the _20 variants.
Default: edusign-test
- EDUSIGN_API_USERNAME_20
Username for Basic Auth for the eduSign API.
Default: dummy
- EDUSIGN_API_PASSWORD_20
Password for Basic Auth for the eduSign API.
Default: dummy
- SIGN_REQUESTER_ID
This is providedto the integration API to construct the sign request. It should be set to the SAML entity ID of the sign service as an SP, but any string will do as long as the integration API and the frontend app have the same value.
Default: https://sig.idsec.se/shibboleth
- VALIDATOR_API_BASE_URL
URL of the signature validator service.
Default: https://sig.idsec.se/sigval/
- SIGNER_ATTRIBUTES_20
The attributes that are displayed in the image representation of the signature, given as
<name>,<friendlyName>
, and separated by semicolons.Default: urn:oid:2.16.840.1.113730.3.1.241,displayName
- AUTHN_ATTRIBUTES_20
The attributes that are used to make sure that the identity used for signing is the same as the one used for authentication.
Default: urn:oid:1.3.6.1.4.1.5923.1.1.1.6,eduPersonPrincipalName
- SCOPE_WHITELIST
Comma separated list of domain names, so users having an eppn belonging to those domains can start signing documents.
Default: sunet.se,eduid.se
- USER_BLACKLIST
Comma separated list of eppn's, so users identified by them cannot start signing documents.
Default: [email protected]
- USER_WHITELIST
Comma separated list of eppn's, so users identified by them can start signing documents.
Default: [email protected]
- STORAGE_CLASS_PATH
Dotted path to the Python class implementing the backend for the sorage of documents with invitations to sign.
Default: edusign_webapp.document.storage.local.LocalStorage
- LOCAL_STORAGE_BASE_DIR
Only needed when STORAGE_CLASS_PATH is set to edusign_webapp.document.storage.local.LocalStorage. Filesystem path pointing to a directory in which to store documents.
Default: /tmp
- AWS_ENDPOINT_URL
Only needed when STORAGE_CLASS_PATH is set to edusign_webapp.document.storage.s3.S3Storage. URL to access S3 bucket. If using GCP, set to https://storage.googleapis.com. If using AWS, do not set it, or set to none
Default: none
- AWS_ACCESS_KEY
Only needed when STORAGE_CLASS_PATH is set to edusign_webapp.document.storage.s3.S3Storage. AWS access key, to be set when STORAGE_CLASS_PATH is set to edusign_webapp.document.storage.s3.S3Storage.
Default: dummy
- AWS_SECRET_ACCESS_KEY
Only needed when STORAGE_CLASS_PATH is set to edusign_webapp.document.storage.s3.S3Storage. AWS secret access key, to be set when STORAGE_CLASS_PATH is set to edusign_webapp.document.storage.s3.S3Storage.
Default: dummy
- AWS_REGION_NAME
Only needed when STORAGE_CLASS_PATH is set to edusign_webapp.document.storage.s3.S3Storage. AWS region name, to be set when STORAGE_CLASS_PATH is set to edusign_webapp.document.storage.s3.S3Storage.
Default: eu-north-1
- AWS_BUCKET_NAME
Only needed when STORAGE_CLASS_PATH is set to edusign_webapp.document.storage.s3.S3Storage. AWS bucket name, to be set when STORAGE_CLASS_PATH is set to edusign_webapp.document.storage.s3.S3Storage.
Default: edusign-storage
- DOC_METADATA_CLASS_PATH
Dotted path to the Python class implementing the backend for the metadata of invitations to sign.
Default: edusign_webapp.document.metadata.sqlite.SqliteMD
- SQLITE_MD_DB_PATH
Only needed when DOC_METADATA_CLASS_PATH is set to edusign_webapp.document.metadata.sqlite.SqliteMD. Filesystem path pointing to a sqlite db.
Default: /tmp/test.db
- REDIS_URL
Only needed when DOC_METADATA_CLASS_PATH is set to edusign_webapp.document.metadata.redis.RedisMD. URL to connect to Redis.
Default: redis://localhost:6379/0.
- MAX_SIGNATURES
The maximum number of signatures that fit in a document.
Default: 10
- UI_SEND_SIGNED
Default value for the invitation form field indicating whether to send the final signed document by mail.
Default: True
- UI_SKIP_FINAL
Default value for the invitation form field indicating whether the inviting user should provide a final signature.
Default: True
- UI_ORDERED_INVITATIONS
Default value for the invitation form field indicating whether the invitations should be sent in order.
Default: False
In addition it is necessary to provide the app with access to some SMTP server, setting the variables indicated here.
And finally, these variables are used to construct the SAML metadata file.
- MD_ENTITY_ID
SAML entityID
Default: https://edusign.sunet.se/shibboleth
- MD_ENTITY_CATEGORIES
SAML entity categories
- MD_DISPLAY_NAMES
SAML MDUI display names
Default: sv,SUNET eduSIGN - tjänst för e-signaturer;en,SUNET eduSIGN Service
- MD_DESCRIPTIONS
SAML MDUI descriptions
Default: sv,SUNET eduSIGN gör det enkelt att arbeta med e-signaturer;en,SUNET eduSIGN Service makes it easy to electronically sign documents
- MD_INFORMATION_URLS
SAML MDUI information URLs
Default: sv,https://www.sunet.se/services/sakerhet/edusign/;en,https://www.sunet.se/services/sakerhet/edusign/
- MD_PRIVACY_STATEMENT_URLS
SAML MDUI privacy statement URLs
- MD_SHIBBOLETH_LOCATION
Base URL for all shibboleth locations (assertion consumer service, etc.)
- MD_SIGNING_CERTIFICATE
Public key of the certificate used for signing
Default: cert for https://dev.edusign.sunet.se/shibboleth
- MD_ENCRYPTION_CERTIFICATE
Public key of the certificate used for encryption (can be the same used for signing)
Default: cert for https://dev.edusign.sunet.se/shibboleth
- MD_SERVICE_NAMES
SAML attribute consuming service names
Default: sv,SUNET eduSIGN - tjänst för e-signaturer;en,SUNET eduSIGN Service
- MD_ATTRIBUTES
Requested attributes
Default: eduPersonPrincipalName,urn:oid:1.3.6.1.4.1.5923.1.1.1.6;sn,urn:oid:2.5.4.4;givenName,urn:oid:2.5.4.42;displayName,urn:oid:2.16.840.1.113730.3.1.241;eduPersonAssurance,urn:oid:1.3.6.1.4.1.5923.1.1.1.11;mail,urn:oid:0.9.2342.19200300.100.1.3;mailLocalAddress,urn:oid:2.16.840.1.113730.3.1.13
- MD_ORGANIZATION_NAMES
SAML Organization names
Default: sv,Vetenskapsrådet;en,The Swedish Research Council
- MD_ORGANIZATION_DISPLAY_NAMES
SAML Organization display names
Default: sv,Sunet;en,Sunet
- MD_ORGANIZATION_URLS
SAML Organization URLs
Default: sv,https://www.sunet.se;en,https://www.sunet.se
- MD_TECHNICAL_CONTACT_NAME
SAML Technical contact name
Default: SUNET
- MD_TECHNICAL_CONTACT_EMAIL
SAML Technical contact email
Default: mailto:[email protected]
- MD_ADMINISTRATIVE_CONTACT_NAME
SAML Administrative contact name
Default: SUNET
- MD_ADMINISTRATIVE_CONTACT_EMAIL
SAML Administrative contact email
Default: mailto:[email protected]
- MD_SUPPORT_CONTACT_NAME
SAML support contact name
Default: SUNET
- MD_SUPPORT_CONTACT_EMAIL
SAML support contact email
Default: mailto:[email protected]
- MD_SECURITY_CONTACT_NAME
SAML security contact name
Default: SUNET
- MD_SECURITY_CONTACT_EMAIL
SAML security contact email
Default: mailto:[email protected]
- SP_HOSTNAME
FQDN for the service, as used in the SSL certificate for the NGINX.
Default: sp.edusign.docker
- MAX_FILE_SIZE
Maximum size of uploadable documents, in a format that NGINX understands, e.g. 20M.
Default: 20M
- PROXY_NETWORK
- If the NGINX server is behind a proxy server / load balancer, it needs to know the network address(es) of the proxy to be able to recover the real IP from the client. It can be set to an IP address / hostname/ CIDR / unix socket.
- DISCO_URL
URL of SAML discovery service to provide to Shibboleth SP.
Default: https://md.nordu.net/role/idp.ds
- MDQ_BASE_URL:
Base URL for an MDQ server, used
No Default.
- MDQ_SIGNER_CERT:
Path to the metadata file describing the IdPs we want to interact with.
No Default.
- BACKEND_HOST
The hostname of the container running the backend WSGI app.
Default: www
- BACKEND_PORT
The TCP port the WSGI app is listening at.
Default: 8080
- BACKEND_SCHEME
The protocol to access the WSGI app.
Default: http
- ACMEPROXY
- For the .well-known/acme-challenge nginx location for letsencrypt.
The anonymous home page at the root of the site takes its content from markdown documents. There are English and Swedish default md docs under version control, in the edusign-app repo. These can be overriden by documents /etc/edusign/home-en.md and /etc/edusign/home-sv.md, in the edusign-app container. edusign-app:/backend/src/edusign_webapp/md/