diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 5d0153a2f..816a50477 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -49,42 +49,41 @@ build-dev-image: - tags - /^version/.*$/ - isort: script: - isort -c -sg env stage: test services: - - postgres:latest - - redis:latest + - postgres:latest + - redis:latest migrations: script: - python manage.py migrate stage: test services: - - postgres:latest - - redis:latest + - postgres:latest + - redis:latest prospector: script: - prospector stage: test services: - - postgres:latest - - redis:latest + - postgres:latest + - redis:latest pylint: script: - pylint passbook stage: test services: - - postgres:latest - - redis:latest + - postgres:latest + - redis:latest coverage: script: - ./scripts/coverage.sh stage: test services: - - postgres:latest - - redis:latest + - postgres:latest + - redis:latest build-passbook-server: stage: build @@ -98,6 +97,18 @@ build-passbook-server: only: - tags - /^version/.*$/ +build-docs: + stage: build + image: + name: gcr.io/kaniko-project/executor:debug + entrypoint: [""] + before_script: + - echo "{\"auths\":{\"docker.beryju.org\":{\"auth\":\"$DOCKER_AUTH\"}}}" > /kaniko/.docker/config.json + script: + - /kaniko/executor --context $CI_PROJECT_DIR --dockerfile $CI_PROJECT_DIR/docs/Dockerfile --destination docker.beryju.org/passbook/docs:latest --destination docker.beryju.org/passbook/docs:0.7.4-beta + only: + - tags + - /^version/.*$/ build-passbook-static: stage: build image: diff --git a/docs/Dockerfile b/docs/Dockerfile new file mode 100644 index 000000000..1bee7d1ca --- /dev/null +++ b/docs/Dockerfile @@ -0,0 +1,14 @@ +FROM python:3.7-slim-buster as builder + +WORKDIR /mkdocs + +RUN pip install mkdocs mkdocs-material + +COPY docs/ docs +COPY mkdocs.yml . + +RUN mkdocs build + +FROM nginx + +COPY --from=builder /mkdocs/site /usr/share/nginx/html diff --git a/docs/factors.md b/docs/factors.md new file mode 100644 index 000000000..8e55930c4 --- /dev/null +++ b/docs/factors.md @@ -0,0 +1,23 @@ +# Factors + +A factor represents a single authenticating factor for a user. Common examples of this would be a password or an OTP. These factors can be combined in any order, and can be dynamically enabled using policies. + +## Password Factor + +This is the standard Password Factor. It allows you to select which Backend the password is checked with. here you can also specify which Policies are used to check the password. You can also specify which Factors a User has to pass to recover their account. + +## Dummy Factor + +This factor waits a random amount of time. Mostly used for debugging. + +## E-Mail Factor + +This factor is mostly for recovery, and used in conjunction with the Password Factor. + +## OTP Factor + +This is your typical One-Time Password implementation, compatible with Authy and Google Authenticator. You can enfore this Factor so that every user has to configure it, or leave it optional. + +## Captcha Factor + +While this factor doesn't really authenticate a user, it is part of the Authentication Flow. passbook uses Google's reCaptcha implementation. diff --git a/docs/images/logo.svg b/docs/images/logo.svg new file mode 100644 index 000000000..520954200 --- /dev/null +++ b/docs/images/logo.svg @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/index.md b/docs/index.md new file mode 100755 index 000000000..309acb089 --- /dev/null +++ b/docs/index.md @@ -0,0 +1,31 @@ +# Welcome + +Welcome to the passbook Documentation. passbook is an open-source Identity Provider and Usermanagement software. It can be used as a central directory for users or customers and it can integrate with your existing Directory. + +passbook can also be used as part of an Application to facilitate User Enrollment, Password recovery and Social Login. + +passbook uses the following Terminology: + +### Policy + +A Policy is at a base level a yes/no gate. It will either evaluate to True or False depending on the Policy Kind and settings. For example, a "Group Membership Policy" evaluates to True if the User is member of the specified Group and False if not. This can be used to conditionally apply Factors and grant/deny access. + +### Provider + +A Provider is a way for other Applications to authenticate against passbook. Common Providers are OpenID Connect (OIDC) and SAML. + +### Source + +Sources are ways to get users into passbook. This might be an LDAP Connection to import Users from Active Directory, or an OAuth2 Connection to allow Social Logins. + +### Application + +An application links together Policies with a Provider, allowing you to control access. It also holds Information like UI Name, Icon and more. + +### Factors + +Factors represent Authentication Factors, like a Password or OTP. These Factors can be dynamically enabled using policies. This allows you to, for example, force users from a certain IP ranges to complete a Captcha to authenticate. + +### Property Mappings + +Property Mappings allow you to make Information available for external Applications. For example, if you want to login to AWS with passbook, you'd use Property Mappings to set the User's Roles based on their Groups. diff --git a/docs/installation/docker-compose.md b/docs/installation/docker-compose.md new file mode 100644 index 000000000..af9a29a27 --- /dev/null +++ b/docs/installation/docker-compose.md @@ -0,0 +1,26 @@ +# docker-compose + +This installation Method is for test-setups and small-scale productive setups. + +## Prerequisites + +- docker +- docker-compose + +## Install + +Download the latest `docker-compose.yml` from [here](https://git.beryju.org/BeryJu.org/passbook/raw/master/docker-compose.yml). Place it in a directory of your choice. + +passbook needs to know it's primary URL to create links in E-Mails and set cookies, so you have to run the following command: + +``` +export PASSBOOK_DOMAIN=domain.tld # this can be any domain or IP, it just needs to point to passbook. +``` + +The compose file references the current latest version, which can be overridden with the `SERVER_TAG` Environment variable. + +If you plan to use this setup for production, it is also advised to change the PostgreSQL Password by setting `PG_PASS` to a password of your choice. + +Now you can pull the Docker images needed by running `docker-compose pull`. After this has finished, run `docker-compose up -d` to start passbook. + +passbook will then be reachable on Port 80. You can optionally configure the packaged traefik to use Let's Encrypt for TLS Encryption. diff --git a/docs/installation/install.md b/docs/installation/install.md new file mode 100755 index 000000000..ebd88c387 --- /dev/null +++ b/docs/installation/install.md @@ -0,0 +1,6 @@ +# Installation + +There are two supported ways to install passbook: + +- [docker-compose](docker-compose.md) for test- or small productive setups +- [Kubernetes](./kubernetes.md) for larger Productive setups diff --git a/docs/installation/kubernetes.md b/docs/installation/kubernetes.md new file mode 100644 index 000000000..959437b57 --- /dev/null +++ b/docs/installation/kubernetes.md @@ -0,0 +1,3 @@ +# Kubernetes + +For a mid to high-load Installation, Kubernetes is recommended. passbook is installed using a helm-chart. diff --git a/docs/integrations/as-sp/gitlab/index.md b/docs/integrations/as-sp/gitlab/index.md new file mode 100644 index 000000000..78b4e78ef --- /dev/null +++ b/docs/integrations/as-sp/gitlab/index.md @@ -0,0 +1,53 @@ +# GitLab Integration + +GitLab can authenticate against passbook using SAML. + +## Preparation + +The following placeholders will be used: + +- `gitlab.company` is the FQDN of the GitLab Install +- `passbook.company` is the FQDN of the passbook Install + +Create an application in passbook and note the slug, as this will be used later. Create a SAML Provider with the following Parameters: + +- `ACS URL`: https://gitlab.company/users/auth/saml/callback +- `Audience`: https://gitlab.company +- `Issuer`: https://gitlab.company + +You can of course use a custom Signing Certificate, and adjust the Assertion Length. To get the value for `idp_cert_fingerprint`, you can use a tool like [this](https://www.samltool.com/fingerprint.php). + +## GitLab Configuration + +Paste the following block in your `gitlab.rb` file, after replacing the placeholder values from above. The file is located in `/etc/gitlab`. + +```ruby +gitlab_rails['omniauth_enabled'] = true +gitlab_rails['omniauth_allow_single_sign_on'] = ['saml'] +gitlab_rails['omniauth_sync_email_from_provider'] = 'saml' +gitlab_rails['omniauth_sync_profile_from_provider'] = ['saml'] +gitlab_rails['omniauth_sync_profile_attributes'] = ['email'] +gitlab_rails['omniauth_auto_sign_in_with_provider'] = 'saml' +gitlab_rails['omniauth_block_auto_created_users'] = false +gitlab_rails['omniauth_auto_link_saml_user'] = true +gitlab_rails['omniauth_providers'] = [ + { + name: 'saml', + args: { + assertion_consumer_service_url: 'https://gitlab.company/users/auth/saml/callback', + idp_cert_fingerprint: '4E:1E:CD:67:4A:67:5A:E9:6A:D0:3C:E6:DD:7A:F2:44:2E:76:00:6A', + idp_sso_target_url: 'https://passbook.company/application/saml//login/', + issuer: 'https://gitlab.company', + name_identifier_format: 'urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress', + attribute_statements: { + email: ['urn:oid:1.3.6.1.4.1.5923.1.1.1.6'], + first_name: ['urn:oid:2.5.4.3'], + nickname: ['urn:oid:2.16.840.1.113730.3.1.241'] + } + }, + label: 'passbook' + } +] +``` + +Afterwards, either run `gitlab-ctl reconfigure` if you're running GitLab Omnibus, or restart the container if you're using the container. diff --git a/docs/k8s/deployment.yml b/docs/k8s/deployment.yml new file mode 100644 index 000000000..d480ee22d --- /dev/null +++ b/docs/k8s/deployment.yml @@ -0,0 +1,33 @@ +--- +apiVersion: apps/v1beta2 +kind: Deployment +metadata: + name: passbook-docs + namespace: prod-passbook-docs + labels: + app.kubernetes.io/name: passbook-docs + app.kubernetes.io/managed-by: passbook-docs +spec: + replicas: 1 + selector: + matchLabels: + app.kubernetes.io/name: passbook-docs + template: + metadata: + labels: + app.kubernetes.io/name: passbook-docs + spec: + containers: + - name: passbook-docs + image: "docker.beryju.org/passbook/docs:latest" + ports: + - name: http + containerPort: 80 + protocol: TCP + resources: + limits: + cpu: 10m + memory: 20Mi + requests: + cpu: 10m + memory: 20Mi diff --git a/docs/k8s/ingress.yml b/docs/k8s/ingress.yml new file mode 100644 index 000000000..210826cad --- /dev/null +++ b/docs/k8s/ingress.yml @@ -0,0 +1,21 @@ +--- +apiVersion: extensions/v1beta1 +kind: Ingress +metadata: + labels: + app.kubernetes.io/name: passbook-docs + name: passbook-docs + namespace: prod-passbook-docs +spec: + rules: + - host: docs.passbook.beryju.org + http: + paths: + - backend: + serviceName: passbook-docs-http + servicePort: http + path: / + tls: + - hosts: + - docs.passbook.beryju.org + secretName: passbook-docs-acme diff --git a/docs/k8s/service.yml b/docs/k8s/service.yml new file mode 100644 index 000000000..0e83a1a8a --- /dev/null +++ b/docs/k8s/service.yml @@ -0,0 +1,17 @@ +--- +apiVersion: v1 +kind: Service +metadata: + name: passbook-docs-http + namespace: prod-passbook-docs + labels: + app.kubernetes.io/name: passbook-docs +spec: + type: ClusterIP + ports: + - port: 80 + targetPort: http + protocol: TCP + name: http + selector: + app.kubernetes.io/name: passbook-docs diff --git a/docs/policies.md b/docs/policies.md new file mode 100644 index 000000000..c6ed04109 --- /dev/null +++ b/docs/policies.md @@ -0,0 +1,68 @@ +# Policies + +## Kinds + +There are two different Kind of policies, a Standard Policy and a Password Policy. Normal Policies just evaluate to True or False, and can be used everywhere. Password Policies apply when a Password is set (during User enrollment, Recovery or anywhere else). These policies can be used to apply Password Rules like length, etc. The can also be used to expire passwords after a certain amount of time. + +## Standard Policies + +--- + +### Group-Membership Policy + +This policy evaluates to True if the current user is a Member of the selected group. + +### Reputation Policy + +passbook keeps track of failed login attempts by Source IP and Attempted Username. These values are saved as scores. Each failed login decreases the Score for the Client IP as well as the targeted Username by one. + +This policy can be used to for example prompt Clients with a low score to pass a Captcha before they can continue. + +### Field matcher Policy + +This policy allows you to evaluate arbitrary comparisons against the User instance. Currently supported fields are: + +- Username +- E-Mail +- Name +- Is_active +- Date joined + +Any of the following operations are supported: + +- Starts with +- Ends with +- Contains +- Regexp (standard Python engine) +- Exact + +### SSO Policy + +This policy evaluates to True if the current Authentication Flow has been initiated through an external Source, like OAuth and SAML. + +### Webhook Policy + +This policy allows you to send an arbitrary HTTP Request to any URL. You can then use JSONPath to extract the result you need. + +## Password Policies + +--- + +### Password Policy + +This Policy allows you to specify Password rules, like Length and required Characters. +The following rules can be set: + +- Minimum amount of Uppercase Characters +- Minimum amount of Lowercase Characters +- Minimum amount of Symbols Characters +- Minimum Length +- Symbol charset (define which characters are counted as symbols) + +### Have I Been Pwned Policy + +This Policy checks the hashed Password against the [Have I Been Pwned](https://haveibeenpwned.com/) API. This only sends the first 5 characters of the hashed password. The remaining comparison is done within passbook. + +### Password-Expiry Policy + +This policy can enforce regular password rotation by expiring set Passwords after a finite amount of time. This forces users to set a new password. diff --git a/docs/property-mappings.md b/docs/property-mappings.md new file mode 100644 index 000000000..0eebcb0d7 --- /dev/null +++ b/docs/property-mappings.md @@ -0,0 +1,21 @@ +# Property Mappings + +Property Mappings allow you to pass information to external Applications. For example, pass the current user's Groups as a SAML Parameter. Property Mappings are also used to map Source fields to passbook fields, for example when using LDAP. + +## SAML Property Mapping + +SAML Property Mappings allow you embed Information into the SAML AuthN Request. THis Information can then be used by the Application to assign permissions for example. + +You can find examples [here](integrations/) + +## LDAP Property Mapping + +LDAP Property Mappings are used when you define a LDAP Source. These Mappings define which LDAP Property maps to which passbook Property. By default, these mappings are created: + +- Autogenerated LDAP Mapping: givenName -> first_name +- Autogenerated LDAP Mapping: mail -> email +- Autogenerated LDAP Mapping: name -> name +- Autogenerated LDAP Mapping: sAMAccountName -> username +- Autogenerated LDAP Mapping: sn -> last_name + +These are configured for the most common LDAP Setups. diff --git a/docs/providers.md b/docs/providers.md new file mode 100644 index 000000000..8e896e402 --- /dev/null +++ b/docs/providers.md @@ -0,0 +1,16 @@ +# Providers + +Providers allow external Applications to authenticate against passbook and use its User Information. + +## OpenID Provider + +This provider uses the commonly used OpenID Connect variation of OAuth2. + +## OAuth2 Provider + +This provider is slightly different than the OpenID Provider. While it uses the same basic OAuth2 Protocol, it provides a GitHub-compatible Endpoint. This allows you to integrate Applications, which don't support Custom OpenID Providers. +The API exposes Username, E-Mail, Name and Groups in a GitHub-compatible format. + +## SAML Provider + +This provider allows you to integrate Enterprise Software using the SAML2 Protocol. It supports signed Requests. This Provider also has [Property Mappings](property-mappings.md#saml-property-mapping), which allows you to expose Vendor-specific Fields. diff --git a/docs/sources.md b/docs/sources.md new file mode 100644 index 000000000..66193eaf7 --- /dev/null +++ b/docs/sources.md @@ -0,0 +1,39 @@ +# Sources + +Sources allow you to connect passbook to an existing User directory. They can also be used for Social-Login, using external Providers like Facebook, Twitter, etc. + +## Generic OAuth Source + +**All Integration-specific Sources are documented in the Integrations Section** + +This source allows users to enroll themselves with an External OAuth-based Identity Provider. The Generic Provider expects the Endpoint to return OpenID-Connect compatible Information. Vendor specific Implementations have their own OAuth Source. + +- Policies: Allow/Forbid Users from linking their Accounts with this Provider +- Request Token URL: This field is used for OAuth v1 Implementations and will be provided by the Provider. +- Authorization URL: This value will be provided by the Provider. +- Access Token URL: This value will be provided by the Provider. +- Profile URL: This URL is called by passbook to retrieve User information upon successful authentication. +- Consumer key/Consumer secret: These values will be provided by the Provider. + +## SAML Source + +This source allows passbook to act as a SAML Service Provider. Just like the SAML Provider, it supports signed Requests. Vendor specific documentation can be found in the Integrations Section + +## LDAP Source + +This source allows you to import Users and Groups from an LDAP Server + +- Server URI: URI to your LDAP Server/Domain Controller +- Bind CN: CN to bind as, this can also be a UPN in the format of `user@domain.tld` +- Bind password: Password used during the bind process +- Enable Start TLS: Enables StartTLS functionality. To use SSL instead, use port `636` +- Base DN: Base DN used for all LDAP queries +- Addition User DN: Prepended to Base DN for User-queries. +- Addition Group DN: Prepended to Base DN for Group-queries. +- User object filter: Consider Objects matching this filter to be Users. +- Group object filter: Consider Objects matching this filter to be Groups. +- User group membership field: Field which contains Groups of user. +- Object uniqueness field: Field which contains a unique Identifier. +- Sync groups: Enable/disable Group synchronization. Groups are synced in the background every 5 minutes. +- Sync parent group: Optionally set this Group as parent Group for all synced Groups (allows you to, for example, import AD Groups under a root `imported-from-ad` group.) +- Property mappings: Define which LDAP Properties map to which passbook Properties. The default set of Property Mappings is generated for Active Directory. See also [LDAP Property Mappings](property-mappings.md#ldap-property-mapping) diff --git a/mkdocs.yml b/mkdocs.yml new file mode 100644 index 000000000..e4dc1cf5a --- /dev/null +++ b/mkdocs.yml @@ -0,0 +1,28 @@ +site_name: passbook Docs +site_url: https://docs.passbook.beryju.org +copyright: "Copyright © 2019 - 2020 BeryJu.org" + +nav: + - Home: index.md + - Installation: + - Installation: installation/install.md + - docker-compose: installation/docker-compose.md + - Kubernetes: installation/kubernetes.md + - Sources: sources.md + - Providers: providers.md + - Property Mappings: property-mappings.md + - Factors: factors.md + - Policies: policies.md + - Integrations: + - as Provider: + - GitLab: integrations/as-sp/gitlab/index.md + +repo_name: "BeryJu.org/passbook" +repo_url: https://git.beryju.org/BeryJu.org/passbook +theme: + name: "material" + logo: "images/logo.svg" + +markdown_extensions: + - toc: + permalink: "ΒΆ" diff --git a/passbook/admin/forms/base.py b/passbook/admin/forms/base.py index 3d6462aa3..9950a6e67 100644 --- a/passbook/admin/forms/base.py +++ b/passbook/admin/forms/base.py @@ -1,4 +1,4 @@ -"""p2 form helpers""" +"""passbook form helpers""" from django import forms from passbook.admin.fields import YAMLField diff --git a/passbook/policies/reputation/signals.py b/passbook/policies/reputation/signals.py index cb552236e..570396ef9 100644 --- a/passbook/policies/reputation/signals.py +++ b/passbook/policies/reputation/signals.py @@ -25,11 +25,13 @@ def update_score(request, username, amount): user_score.save() LOGGER.debug("Updated score", amount=amount, for_user=username) + @receiver(user_login_failed) def handle_failed_login(sender, request, credentials, **kwargs): """Lower Score for failed loging attempts""" update_score(request, credentials.get('username'), -1) + @receiver(user_logged_in) def handle_successful_login(sender, request, user, **kwargs): """Raise score for successful attempts"""