Skip to content

Commit

Permalink
Merge pull request #13 from ethho/dev-385-debug
Browse files Browse the repository at this point in the history
DEV-385: Fix unwrap when provider response fails
  • Loading branch information
yambottle committed Jan 17, 2024
2 parents c10d2b2 + 4f75c5d commit 276ae3a
Show file tree
Hide file tree
Showing 15 changed files with 725 additions and 195 deletions.
3 changes: 3 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
pam-oidc/target
.git
.gitignore
33 changes: 33 additions & 0 deletions .github/workflows/release.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
name: Create Versioned Release

on:
push:
branches:
- master
tags:
- 'v*.*.*'

jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Build
run: |
export DOCKER_TAG="${{ github.ref_name }}"
docker compose build builder
mkdir -p pam-oidc/bin
docker compose cp builder:/tmp/pam-oauth2/libpam_oidc_gnu.so ./pam-oidc/bin/
docker compose cp builder:/tmp/pam-oauth2/libpam_oidc_musl.so ./pam-oidc/bin/
- name: Touch Changelog
if: startsWith(github.ref, 'refs/tags/')
run: touch "docs/CHANGELOG-${{ github.ref_name }}.md"
- name: Release
uses: softprops/action-gh-release@v1
if: startsWith(github.ref, 'refs/tags/')
with:
files: |
./pam-oidc/bin/libpam_oidc_gnu.so
./pam-oidc/bin/libpam_oidc_musl.so
body_path: docs/CHANGELOG-${{ github.ref_name }}.md
8 changes: 6 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
target
libpam_oidc.yaml
.env
Cargo.lock
*.env
Cargo.lock

# Added by cargo

/target
7 changes: 6 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
{
"editor.rulers": [95]
"editor.rulers": [
95
],
"rust-analyzer.linkedProjects": [
"./pam-oidc/Cargo.toml"
]
}
171 changes: 163 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,19 @@
# Rust Demo
# LibPAM OIDC

![GitHub Release](https://img.shields.io/github/v/release/datajoint-company/pam-oauth2)
![build](https://img.shields.io/github/actions/workflow/status/datajoint-company/pam-oauth2/release)


This repository contains a Pluggable Authentication Module (PAM) to allow authentication against a central OIDC provider.

## Deploy Instructions

1. Acquire (see the [releases](https://github.com/datajoint-company/pam-oauth2/releases) page) or build (see below) the appropriate `libpam_oidc.so` dynamic clib binary for your platform that provides the PAM interface to authenticate via an OIDC provider.
1. Copy `libpam_oidc.so` into the appropriate directory that your system expects new modules to be loaded e.g. on Debian, it is located in `/lib/x86_64-linux-gnu/security/`.
1. Create a service config file within the directory that your system expects for PAM e.g. on Debian, it is located in `/etc/pam.d/`. We can for instance create a service/file called `oidc` with the following contents (note the argument in the 1st line should be the path where `pam_oidc`'s config will be located):
1. The `libpam_oidc_gnu.so` binary is built for GNU/Linux distributions and dynamically links to the OS's glibc library.
2. The `libpam_oidc_musl.so` binary is built for GNU/Linux distributions and statically links to the MUSL library. It sacrifices speed for portability.
2. Copy `libpam_oidc.so` into the appropriate directory that your system expects new PAM modules to be loaded e.g. on some distributions of Debian, it is located in `/lib/x86_64-linux-gnu/security/`, on others it is `/usr/lib64/security/`.
1. Use `ldconfig -p | grep pam` to find the directory on your distribution.
3. Create a service config file within the directory that your system expects for PAM. For example, on Debian, it is located in `/etc/pam.d/`. We can create a service/file at `/etc/pam.d/oidc` with the following contents (note the argument in the 1st line should be the path where `pam_oidc`'s config will be located):

```text
auth sufficient libpam_oidc.so /etc/datajoint/libpam_oidc.yaml
Expand All @@ -13,11 +22,118 @@

See [service_example](./config/service_example) for more info.

1. In the path provided to the service config, create a config file for `pam_oidc`. See [libpam_oidc_example.yaml](./config/libpam_oidc_example.yaml) for more info.
1. Configure your PAM-compatible application/service to point to the `oidc` service we just created. For a few examples, see [test.sh](./tests/test.sh).
4. In the path provided to the service config, create a config file for `pam_oidc`. See [libpam_oidc_example.yaml](./config/libpam_oidc_example.yaml) for more info.
5. Configure your PAM-compatible application/service to point to the `oidc` service we just created. For example, we can [configure Percona MySQL](https://docs.percona.com/percona-server/8.0/pam-plugin.html) to use PAM. See [MySQL testing](#3-mysql-tests) for an example.

## Developer Instructions

Since v0.1.5, the test and build have been moved to a [Docker Compose](./docker-compose.yml) file.

### Build

Since v0.1.5, releases are built in the [`builder` service](./docker-compose.yml). See the [`builder` Dockerfile`](./docker/builder.dockerfile) for details. Building with Docker Compose requires a `.env` file at the repository root (it can be empty). We can build the binaries for targets `x86_64-unknown-linux-gnu` and `x86_64-unknown-linux-musl`, respectively:

```bash
docker compose build builder
mkdir -p pam-oidc/bin
docker compose cp builder:/tmp/pam-oauth2/libpam_oidc_gnu.so ./pam-oidc/bin/
docker compose cp builder:/tmp/pam-oauth2/libpam_oidc_musl.so ./pam-oidc/bin/
```

### Testing

There are three types of tests:

1. Unit tests
2. PAM integration tests
3. MySQL tests

Some testing modes use [Docker Compose](https://docs.docker.com/compose/).
The ones that do require a `.env` file defining environment variables for the [`percona` service](./docker-compose.yml):

```bash
DJ_AUTH_USER=
DJ_AUTH_PASSWORD=
DJ_AUTH_TOKEN=
```

#### 1. Unit Tests

Unit test development is _WIP_.
Unit tests are written in Rust and only test the Rust code. They do not test the PAM integration.
Run these tests using `cargo test`.

#### 2. PAM Integration Tests

These integration tests cover the PAM integration, but none of the services that use PAM such as SSH or MySQL.
In other words, they test all but the last step in the [Deploy Instructions](#deploy-instructions).
These tests run the [`test.py`](./tests/test.py) script in the [`percona` service](./docker-compose.yml), using the [`python-pam`](https://pypi.org/project/python-pam/) library to simulate PAM calls.
To run these tests, create the `.env` file as mentioned previously, then run:

```bash
docker compose run --build percona python3 /opt/test.py
# dkc run -it percona python3 /opt/test.py
# [+] Creating 1/0
# ✔ Container pam-oauth2-builder Created 0.0s
# [+] Running 1/1
# ✔ Container pam-oauth2-builder Started 0.4s
# Authenticated (pam_unix)? True
# Reason (pam_unix): Success
# Authenticating with DJ_AUTH_USER='demouser'
# [2024-01-17 03:51:23.334][pam-oidc][0.1.4][INFO][656398155]: Auth detected. Proceeding...
# [2024-01-17 03:51:23.335][pam-oidc][0.1.4][INFO][656398155]: Inputs read.
# [2024-01-17 03:51:23.335][pam-oidc][0.1.4][INFO][656398155]: Check as password.
# [2024-01-17 03:51:23.651][pam-oidc][0.1.4][INFO][656398155]: Verifying token.
# [2024-01-17 03:51:23.896][pam-oidc][0.1.4][INFO][656398155]: Auth success!
# Authenticated (oidc user:pass)? True
# Reason (oidc user:pass): Success
# Authenticating with DJ_AUTH_USER='demouser'
# [2024-01-17 03:51:23.897][pam-oidc][0.1.4][INFO][656398155]: Auth detected. Proceeding...
# [2024-01-17 03:51:23.897][pam-oidc][0.1.4][INFO][656398155]: Inputs read.
# [2024-01-17 03:51:23.897][pam-oidc][0.1.4][INFO][656398155]: Check as token.
# [2024-01-17 03:51:23.897][pam-oidc][0.1.4][INFO][656398155]: Verifying token.
# [2024-01-17 03:51:24.137][pam-oidc][0.1.4][INFO][656398155]: Auth success!
# Authenticated (oidc user:token)? True
# Reason (oidc user:token): Success
```

The `test.py` script will try authenticating in three ways:

1. Using the `pam_unix` module as user `ap_user`. This checks that PAM is working in general.
2. Using the `oidc` module as user `DJ_AUTH_USER` with password `DJ_AUTH_PASSWORD`. This checks that the `pam_oidc` module is working with the password flow.
3. Using the `oidc` module as user `DJ_AUTH_USER` with token `DJ_AUTH_TOKEN`. This checks that the `pam_oidc` module is working with the token flow.

#### 3. MySQL Tests

These tests cover the PAM integration with MySQL.
Create the `.env` file and issue the following commands, replacing `demouser` with the value of `DJ_AUTH_USER` in the `.env` file:

```bash
docker compose up --build -d percona
# Wait until service is healthy, then create demouser
docker compose exec percona mysql -hlocalhost -uroot -ppassword -e "CREATE USER 'demouser'@'%' IDENTIFIED WITH auth_pam AS 'oidc';"
# Check if the auth_pam plugin is enabled in Percona
docker compose exec percona mysql -hlocalhost -uroot -ppassword -e "SHOW PLUGINS;" | grep auth_pam
# auth_pam ACTIVE AUTHENTICATION auth_pam.so GPL
# Login as demouser
docker compose exec percona mysql -hlocalhost -udemouser -p'password_or_token_in_dot_env' -e "SELECT 1;"
# +---+
# | 1 |
# +---+
# | 1 |
# +---+
```

Successful login will return a table with a single row containing the value `1`.
This indicates that the `pam_oidc` module is working with MySQL.

## Old Developer Instructions

Below are the old instructions for building and testing the `pam_oidc` module.

<details>
<summary>Click to expand</summary>

### Build

```bash
Expand All @@ -35,7 +151,44 @@ DJ_AUTH_TOKEN=
```
See tests in `tests` subdirectory. The header comment gives hints how to run them.

## --- Old Notes ---
### Testing `pam_unix` Plugin in Percona

Following [Percona blog post](https://www.percona.com/blog/getting-percona-pam-to-work-with-percona-server-its-client-apps/):

```console
alias dkc='docker compose'
dkc up --build -d percona
dkc exec -it percona mysql -hlocalhost -uroot -ppassword -e "SHOW PLUGINS;" | grep auth_pam
auth_pam ACTIVE AUTHENTICATION auth_pam.so GPL
dkc exec -it percona mysql -hlocalhost -uroot -ppassword
mysql: [Warning] Using a password on the command line interface can be insecure.
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 19
Server version: 8.0.34-26 Percona Server (GPL), Release 26, Revision 0fe62c85

Copyright (c) 2009-2023 Percona LLC and/or its affiliates
Copyright (c) 2000, 2023, Oracle and/or its affiliates.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> CREATE USER ap_user IDENTIFIED WITH auth_pam;
Query OK, 0 rows affected (0.04 sec)

mysql> DELETE FROM mysql.user WHERE USER='';
Query OK, 0 rows affected (0.00 sec)

mysql> FLUSH PRIVILEGES;
Query OK, 0 rows affected (0.02 sec)

mysql>
Bye
dkc exec -it percona mysql -hlocalhost -uap_user -ppassword
# Success
```

### Start

Expand Down Expand Up @@ -72,16 +225,18 @@ cargo build
echo shh | PAM_TYPE=auth PAM_USER=raphael ./pam_oidc/target/release/pam_oidc ./sample.yaml


## cross-compile
### cross-compile

rustup target add x86_64-unknown-linux-gnu
rustup target add x86_64-unknown-linux-musl
rustup show
cargo build --target x86_64-unknown-linux-musl --features vendored
cargo build --release --target x86_64-unknown-linux-musl

## testing (current on 07/01/21)
### testing (current on 07/01/21)

cp pam-oidc/test /etc/pam.d/
cp pam-oidc/target/debug/libpam_oidc.so /lib/x86_64-linux-gnu/security/
python3 /workspace/test.py

</details>
3 changes: 3 additions & 0 deletions config/pam_unix
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
auth required pam_warn.so
auth required pam_unix.so audit
account required pam_unix.so audit
2 changes: 1 addition & 1 deletion config/service_example
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
auth sufficient libpam_oidc.so /etc/datajoint/libpam_oidc.yaml
account optional libpam_oidc.so
account optional libpam_oidc.so
48 changes: 32 additions & 16 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,19 +1,35 @@
# docker compose up --build
# docker buildx bake --set "*.platform=linux/amd64" --load
version: "2.4"
# docker compose up --build percona
version: "3"
services:
app:
build: .
image: pam_oidc:v0.1.4
percona:
container_name: pam-oauth2-percona
build:
context: .
dockerfile: docker/percona.dockerfile
args:
- BUILDER_TAG=${DOCKER_TAG:-v0.1.5}
environment:
- DJ_AUTH_USER
- DJ_AUTH_PASSWORD
- DJ_AUTH_TOKEN
# - RUSTFLAGS=-C link-arg=-undefined
# - RUSTFLAGS=-C target-feature=-crt-static
command: tail -f /dev/null
- MYSQL_ROOT_PASSWORD=password
env_file:
- .env
command: /docker-entrypoint.sh mysqld
healthcheck:
test: [ "CMD", "mysqladmin", "ping", "-h", "localhost" ]
timeout: 30s
retries: 5
interval: 15s
ports:
- 3306:3306
volumes:
- ./config/service_example:/etc/pam.d/oidc # add a 'oidc' config that utilizes pam_oidc
- ./config/libpam_oidc.yaml:/etc/datajoint/libpam_oidc.yaml # add pam_oidc-specific config
- ./tests/test.py:/workspace/test.py # python test
- ./pam-oidc:/workspace/pam-oidc # mount source for dev
- ./tests/test.py:/opt/test.py
- ./config/libpam_oidc.yaml:/etc/datajoint/libpam_oidc.yaml
depends_on:
builder:
condition: service_completed_successfully
image: datajoint/pam-oauth2-percona:${DOCKER_TAG:-v0.1.5}
builder:
container_name: pam-oauth2-builder
build:
context: .
dockerfile: docker/builder.dockerfile
image: datajoint/pam-oauth2-builder:${DOCKER_TAG:-v0.1.5}
File renamed without changes.
19 changes: 19 additions & 0 deletions docker/builder.dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
FROM mcr.microsoft.com/devcontainers/rust:1.0.7-bullseye
RUN \
apt-get update && \
apt-get install \
musl-tools libssl-dev pkg-config libssl-dev build-essential \
gcc g++ openssl \
libpam0g-dev libpam0g gdb git -y
ENV RUSTFLAGS="-C target-feature=-crt-static"
WORKDIR /tmp/pam-oauth2
COPY pam-oidc /tmp/pam-oauth2/pam-oidc
RUN \
cd pam-oidc && \
rustup target add x86_64-unknown-linux-gnu && \
rustup target add x86_64-unknown-linux-musl && \
rustup show && \
cargo build --release --target x86_64-unknown-linux-musl && \
cargo build --release --target x86_64-unknown-linux-gnu && \
cp target/x86_64-unknown-linux-musl/release/libpam_oidc.so /tmp/pam-oauth2/libpam_oidc_musl.so && \
cp target/x86_64-unknown-linux-gnu/release/libpam_oidc.so /tmp/pam-oauth2/libpam_oidc_gnu.so
25 changes: 25 additions & 0 deletions docker/percona.dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
ARG BUILDER_TAG
FROM datajoint/pam-oauth2-builder:${BUILDER_TAG} as builder
FROM percona:8
USER root
RUN \
yum -y install python3 python3-pip && \
pip3 install python-pam

# Fetch the binary from the release page
# ADD https://github.com/datajoint-company/pam-oauth2/releases/download/0.1.4/libpam_oidc_linux_amd64.so /usr/lib64/security/libpam_oidc.so
# RUN chmod +rx /usr/lib64/security/libpam_oidc.so

# https://www.percona.com/blog/getting-percona-pam-to-work-with-percona-server-its-client-apps/
RUN \
chgrp mysql /etc/shadow && \
chmod g+r /etc/shadow && \
useradd ap_user && \
echo "ap_user:password" | chpasswd
USER mysql:mysql

# https://docs.percona.com/percona-server/8.0/pam-plugin.html#installation
COPY --from=builder /tmp/pam-oauth2/libpam_oidc_gnu.so /usr/lib64/security/libpam_oidc.so
RUN echo 'plugin_load_add = auth_pam.so' >> /etc/my.cnf
COPY config/pam_unix /etc/pam.d/mysqld
COPY config/service_example /etc/pam.d/oidc
3 changes: 3 additions & 0 deletions docs/CHANGELOG-v0.1.5.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
### Fixed

* Fix fatal crash when using invalid access token #13.
Loading

0 comments on commit 276ae3a

Please sign in to comment.