Compare commits

..

1 Commits

Author SHA1 Message Date
Jonathan Perret 8f9a9ed1f0 temporary enable debug logs in production 2025-03-05 17:32:56 +01:00
19 changed files with 168 additions and 221 deletions
+2 -2
View File
@@ -9,9 +9,9 @@ We primarily use GitHub as an issue tracker. If however you're encountering an i
---
Please make sure you have read our [main Readme](https://github.com/proconnect-gouv/oidc2fer).
Please make sure you have read our [main Readme](https://github.com/numerique-gouv/oidc2fer).
Also make sure it was not already answered in [an open or close issue](https://github.com/proconnect-gouv/oidc2fer/issues).
Also make sure it was not already answered in [an open or close issue](https://github.com/numerique-gouv/oidc2fer/issues).
If your question was not covered, and you feel like it should be, fire away! We'd love to improve our docs! 👌
-67
View File
@@ -1,67 +0,0 @@
name: 🐳 Build docker images
on:
push:
branches:
- 'main'
tags:
- 'v*'
pull_request:
branches:
- 'main'
jobs:
build-and-push:
runs-on: ubuntu-latest
permissions:
packages: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Docker meta
id: meta
uses: docker/metadata-action@v5
with:
images: ghcr.io/${{ github.repository }}
tags: |
type=ref,event=branch
type=ref,event=tag
type=ref,event=pr
type=sha,prefix=sha-
# set latest tag for default branch
type=raw,value=latest,enable={{is_default_branch}}
- name: Login to Github Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ github.token }}
- name: Build and push
uses: docker/build-push-action@v6
with:
context: .
target: production
platforms: linux/amd64,linux/arm64
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
notify-argocd:
runs-on: ubuntu-latest
if: |
github.event_name != 'pull_request'
needs:
- build-and-push
steps:
- uses: numerique-gouv/action-argocd-webhook-notification@main
id: notify
with:
deployment_repo_path: "${{ github.repository }}"
argocd_webhook_secret: "${{ secrets.ARGOCD_PREPROD_WEBHOOK_SECRET }}"
argocd_url: "${{ vars.ARGOCD_PREPROD_WEBHOOK_URL }}"
+93
View File
@@ -0,0 +1,93 @@
name: Docker Hub Workflow
on:
workflow_dispatch:
push:
branches:
- 'main'
tags:
- 'v*'
pull_request:
branches:
- 'main'
jobs:
build-and-push:
runs-on: ubuntu-latest
steps:
-
uses: actions/create-github-app-token@v1
id: app-token
with:
app-id: ${{ secrets.APP_ID }}
private-key: ${{ secrets.PRIVATE_KEY }}
owner: ${{ github.repository_owner }}
repositories: "oidc2fer,secrets"
-
name: Checkout repository
uses: actions/checkout@v4
with:
submodules: recursive
token: ${{ steps.app-token.outputs.token }}
-
name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
-
name: Docker meta
id: meta
uses: docker/metadata-action@v5
with:
images: lasuite/oidc2fer
-
name: Load sops secrets
uses: rouja/actions-sops@main
with:
secret-file: secrets/numerique-gouv/oidc2fer/secrets.enc.env
age-key: ${{ secrets.SOPS_PRIVATE }}
-
name: Login to DockerHub
run: echo "$DOCKER_HUB_PASSWORD" | docker login -u "$DOCKER_HUB_USER" --password-stdin
-
name: Build and push
uses: docker/build-push-action@v5
with:
context: .
target: production
platforms: linux/amd64,linux/arm64
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
notify-argocd:
runs-on: ubuntu-latest
if: |
github.event_name != 'pull_request'
needs:
- build-and-push
steps:
-
uses: actions/create-github-app-token@v1
id: app-token
with:
app-id: ${{ secrets.APP_ID }}
private-key: ${{ secrets.PRIVATE_KEY }}
owner: ${{ github.repository_owner }}
repositories: "oidc2fer,secrets"
-
name: Checkout repository
uses: actions/checkout@v4
with:
submodules: recursive
token: ${{ steps.app-token.outputs.token }}
-
name: Load sops secrets
uses: rouja/actions-sops@main
with:
secret-file: secrets/numerique-gouv/oidc2fer/secrets.enc.env
age-key: ${{ secrets.SOPS_PRIVATE }}
-
name: Call argocd github webhook
run: |
data='{"ref": "'$GITHUB_REF'","repository": {"html_url":"'$GITHUB_SERVER_URL'/'$GITHUB_REPOSITORY'"}}'
sig=$(echo -n ${data} | openssl dgst -sha1 -hmac ''${ARGOCD_WEBHOOK_SECRET}'' | awk '{print "X-Hub-Signature: sha1="$2}')
curl -X POST -H 'X-GitHub-Event:push' -H "Content-Type: application/json" -H "${sig}" --data "${data}" $ARGOCD_WEBHOOK_URL
+22
View File
@@ -0,0 +1,22 @@
name: Helmfile lint
run-name: Helmfile lint
on:
pull_request:
branches:
- 'main'
jobs:
helmfile-lint:
runs-on: ubuntu-latest
container:
image: ghcr.io/helmfile/helmfile:latest
steps:
-
uses: numerique-gouv/action-helmfile-lint@main
with:
app-id: ${{ secrets.APP_ID }}
age-key: ${{ secrets.SOPS_PRIVATE }}
private-key: ${{ secrets.PRIVATE_KEY }}
helmfile-src: "src/helm"
repositories: "oidc2fer,secrets"
@@ -1,4 +1,4 @@
name: 💚 CI Tests
name: OIDC2FER Workflow
on:
push:
@@ -23,7 +23,7 @@ jobs:
run: git log
- name: Enforce absence of print statements in code
run: |
! git diff origin/${{ github.event.pull_request.base.ref }}..HEAD -- . ':(exclude).github/**' | grep "print("
! git diff origin/${{ github.event.pull_request.base.ref }}..HEAD -- . ':(exclude)**/oidc2fer.yml' | grep "print("
- name: Check absence of fixup commits
run: |
! git log | grep 'fixup!'
@@ -43,7 +43,7 @@ jobs:
with:
fetch-depth: 0
- name: Check that the CHANGELOG has been modified in the current branch
run: git log --name-only --pretty="" origin/${{ github.event.pull_request.base.ref }}..HEAD | grep CHANGELOG
run: git whatchanged --name-only --pretty="" origin/${{ github.event.pull_request.base.ref }}..HEAD | grep CHANGELOG
lint-changelog:
runs-on: ubuntu-latest
@@ -101,37 +101,3 @@ jobs:
pip install --user .[dev]
- name: Run tests
run: ~/.local/bin/pytest
helmfile-lint:
runs-on: ubuntu-latest
container:
image: ghcr.io/helmfile/helmfile:v0.171.0
steps:
-
uses: actions/create-github-app-token@v1
id: app-token
with:
app-id: ${{ secrets.APP_ID }}
private-key: ${{ secrets.PRIVATE_KEY }}
owner: numerique-gouv
repositories: secrets
-
name: Checkout repository
uses: actions/checkout@v4
with:
submodules: recursive
token: ${{ steps.app-token.outputs.token }}
-
name: Helmfile lint
shell: bash
run: |
mkdir -p ~/.config/sops/age/
echo ${{ secrets.SOPS_PRIVATE }} > ~/.config/sops/age/keys.txt
set -e
HELMFILE=src/helm/helmfile.yaml
environments=$(awk '/environments:/ {flag=1; next} flag && NF {print} !NF {flag=0}' "$HELMFILE" | grep -E '^[[:space:]]{2}[a-zA-Z]+' | sed 's/^[[:space:]]*//;s/:.*//')
for env in $environments; do
echo "################### $env lint ###################"
helmfile -e $env -f src/helm/helmfile.yaml lint || exit 1
echo -e "\n"
done
+1 -1
View File
@@ -1,3 +1,3 @@
[submodule "secrets"]
path = secrets
url = ../../numerique-gouv/secrets
url = ../secrets
-17
View File
@@ -6,23 +6,6 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0),
and this project adheres to
[Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [1.0.8] - 2025-09-10
- fix secrets submodule path following move to `proconnect-gouv` organization
- update other references to `numerique-gouv` organization
- publish Docker images at ghcr.io/proconnect-gouv/oidc2fer
## [1.0.7] - 2025-05-07
- remove unused APT dependencies
- upgrade to gunicorn 23.0.0
## [1.0.6] - 2025-03-11
- upgrade to SATOSA 8.5.1
- allow faculty, staff, employee, researcher, teacher affiliations
## [1.0.5] - 2025-03-06
- add allowlist for URL paths to nginx ingress
- allow customizing log levels per logger
## [1.0.4] - 2024-09-11
- serve static files
+11 -5
View File
@@ -1,11 +1,17 @@
# ---- base image to inherit from ----
FROM python:3.11.12-slim-bookworm AS common
FROM python:3.11-slim-bookworm as common
# Install xmlsec1 dependencies required for xmlsec (for SAML)
# Needs to be kept before the `pip install`
RUN apt-get update && \
apt-get -y upgrade && \
apt-get install -qy --no-install-recommends xmlsec1 && \
apt-get install -y \
pkg-config \
gcc \
xmlsec1 \
libxml2-dev \
libxmlsec1-dev \
libxmlsec1-openssl && \
rm -rf /var/lib/apt/lists/*
# We want the most up-to-date stable pip release
@@ -13,7 +19,7 @@ RUN pip install --upgrade pip
ENV PYTHONUNBUFFERED=1
# Give the "root" group the same permissions AS the "root" user on /etc/passwd
# Give the "root" group the same permissions as the "root" user on /etc/passwd
# to allow a user belonging to the root group to add new users; typically the
# docker user (see entrypoint).
RUN chmod g=u /etc/passwd
@@ -35,7 +41,7 @@ COPY docker/files/usr/local/etc/gunicorn/satosa.py /usr/local/etc/gunicorn/satos
CMD ["gunicorn", "-c", "/usr/local/etc/gunicorn/satosa.py"]
# ---- Development image ----
FROM common AS development
FROM common as development
# Playwright browsers
ENV PLAYWRIGHT_BROWSERS_PATH=/pw-browsers
@@ -61,7 +67,7 @@ COPY ./src/satosa /app/
USER ${DOCKER_USER}
# ---- Production image (keep last so it is the default target) ----
FROM common AS production
FROM common as production
# Copy oidc2fer application (see .dockerignore)
COPY ./src/satosa /app/
+1 -8
View File
@@ -57,19 +57,12 @@ number and date.
## Deploying a release to production (DINUM instance)
1. Make sure the release you want to deploy has been built and appears on
https://github.com/proconnect-gouv/oidc2fer/pkgs/container/oidc2fer
https://hub.docker.com/r/lasuite/oidc2fer/tags .
2. Edit `image/tag` at the top of
`src/helm/env.d/outscale-production/values.oidc2fer.yaml.gotmpl`.
3. Commit and push to `main`.
4. Update the `production` tag and push it.
## Environment variables
| variable | usage |
| --- | --- |
| `LOG_LEVEL` | Sets the log level for the root logger, i.e. the default. Defaults to `INFO`. |
| `LOG_LEVELS` | A JSON object that can be used to set log levels for specific loggers, e.g. `{"satosa.backends.saml2": "DEBUG"}`. Defaults to `{}`. |
## Contributing
This project is intended to be community-driven, so please, do not hesitate to
+1 -1
View File
@@ -13,7 +13,7 @@ services:
BASE_URL: https://satosa.traefik.me
GUNICORN_CMD_ARGS: --workers=2
TEST_E2E: 1
#TEST_E2E_PC: 1
#TEST_E2E_AC: 1
OIDC_DB_URI: redis://redis/0
CLIENT_DB_JSON: |
{
+1 -36
View File
@@ -1,5 +1,4 @@
import datetime
import json
import logging
import os
import sys
@@ -30,8 +29,6 @@ class RequestJSONFormatter(json_log_formatter.JSONFormatter):
url += f"?{record.args['q']}"
return {
"logger": record.name,
"level": record.levelname,
"remote_ip": record.args["h"],
"method": record.args["m"],
"path": url,
@@ -40,7 +37,7 @@ class RequestJSONFormatter(json_log_formatter.JSONFormatter):
"user_agent": record.args["a"],
"referer": record.args["f"],
"duration_in_ms": record.args["M"],
"pid": record.process,
"poppid": record.process,
}
@@ -58,23 +55,11 @@ class DefaultJSONFormatter(json_log_formatter.JSONFormatter):
payload: dict[str, str | int | float] = super().json_record(
message, extra, record
)
payload["logger"] = record.name
payload["level"] = record.levelname
payload["pid"] = record.process
return payload
class NoPingFilter(logging.Filter):
"""
Filters out /ping requests from the access log.
"""
def filter(self, record: logging.LogRecord) -> bool:
return not( record.args.get("m", "") == "GET"
and record.args.get("U", "") == "/ping"
and str(record.args.get("s", "")) == "200" )
bind = ["0.0.0.0:8000"]
name = "satosa"
python_path = "/app"
@@ -91,9 +76,6 @@ accesslog = "-"
errorlog = "-"
loglevel = os.environ.get("LOG_LEVEL", "INFO")
loglevels_json = os.environ.get("LOG_LEVELS", "{}")
loglevels = json.loads(loglevels_json)
logconfig_dict = {
"version": 1,
"disable_existing_loggers": True,
@@ -113,24 +95,7 @@ logconfig_dict = {
"handlers": ["json_request"],
"propagate": False,
"qualname": "gunicorn.access",
"filters": [NoPingFilter()],
},
"satosa.base": {
"level": "INFO",
},
"satosa.state": {
"level": "INFO",
},
"satosa.proxy_server": {
"level": "INFO",
},
"satosa.routing": {
"level": "INFO",
},
"satosa.frontends.ping": {
"level": "INFO",
},
**{k: {"level": v.upper()} for k, v in loglevels.items()},
},
"formatters": {
"json_request": {
@@ -1,7 +1,7 @@
image:
repository: lasuite/oidc2fer
pullPolicy: Always
tag: "v1.0.7"
tag: "v1.0.4"
satosa:
replicas: 2
@@ -9,8 +9,7 @@ satosa:
BASE_URL: https://renater.agentconnect.gouv.fr
GUNICORN_CMD_ARGS: --workers=3
LOG_LEVEL: INFO
LOG_LEVELS: '{ "satosa.backends.saml2": "DEBUG" }'
LOG_LEVEL: debug
SAML2_DISCOVERY_URL: https://discovery.renater.fr/agentconnect/
SAML2_METADATA_URL: https://pub.federation.renater.fr/metadata/renater/main/main-idps-renater-metadata.xml
@@ -1,7 +1,7 @@
image:
repository: ghcr.io/proconnect-gouv/oidc2fer
repository: lasuite/oidc2fer
pullPolicy: Always
tag: "latest"
tag: "main"
satosa:
replicas: 2
@@ -9,8 +9,7 @@ satosa:
BASE_URL: https://oidc2fer-staging.beta.numerique.gouv.fr
GUNICORN_CMD_ARGS: --workers=3
LOG_LEVEL: DEBUG
LOG_LEVELS: '{ "satosa.backends.saml2": "DEBUG" }'
LOG_LEVEL: debug
SAML2_DISCOVERY_URL: https://discovery.renater.fr/test/
SAML2_METADATA_URL: https://pub.federation.renater.fr/metadata/test/preview/preview-idps-test-metadata.xml
+4 -7
View File
@@ -1,6 +1,5 @@
{{- if .Values.ingress.enabled -}}
{{- $fullName := include "oidc2fer.fullname" . -}}
{{- $port := .Values.satosa.service.port -}}
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
@@ -34,15 +33,13 @@ spec:
- host: {{ .Values.ingress.host | quote }}
http:
paths:
{{- range .Values.ingress.paths }}
- path: {{ .path | quote }}
pathType: {{ .pathType }}
- path: /
pathType: Prefix
backend:
service:
name: {{ $fullName }}-satosa
name: {{ include "oidc2fer.satosa.fullname" . }}
port:
number: {{ $port }}
{{- end }}
number: {{ .Values.satosa.service.port }}
{{- with .Values.ingress.customBackends }}
{{- toYaml . | nindent 10 }}
{{- end }}
+1 -6
View File
@@ -33,9 +33,7 @@ ingress:
enabled: false
className: null
host: oidc2fer.example.com
paths:
- path: '/$|^/\.well-known/openid-configuration$|^/images/|^/Saml2/|^/OIDC/|^/ping$'
pathType: ImplementationSpecific # enables regex matching
path: /
## @param ingress.hosts Additional host to configure for the Ingress
hosts: []
# - chart-example.local
@@ -50,9 +48,6 @@ ingress:
## @param ingress.customBackends Add custom backends to ingress
customBackends: []
annotations:
nginx.ingress.kubernetes.io/use-regex: "true"
## @section satosa
satosa:
@@ -3,11 +3,11 @@ name: Saml2
config:
disco_srv: !ENV SAML2_DISCOVERY_URL
entityid_endpoint: true
mirror_force_authn: false
memorize_idp: false
use_memorized_idp_when_force_authn: false
send_requester_id: false
enable_metadata_reload: false
mirror_force_authn: 'no'
memorize_idp: 'no'
use_memorized_idp_when_force_authn: 'no'
send_requester_id: 'no'
enable_metadata_reload: 'no'
acs_selection_strategy: prefer_matching_host
sp_config:
name: Passerelle OIDC vers FER
@@ -13,8 +13,4 @@ config:
# later in the PrimaryIdentifier processor.
- ".+"
eduPersonAffiliation:
- "^faculty$"
- "^staff$"
- "^employee$"
- "^researcher$"
- "^teacher$"
+6 -6
View File
@@ -23,18 +23,18 @@ license = { file = "LICENSE" }
readme = "README.md"
requires-python = ">=3.11"
dependencies = [
"SATOSA==8.5.1",
"gunicorn==23.0.0",
"SATOSA==8.4.0",
"gunicorn==22.0.0",
"redis==5.0.4",
"JSON-log-formatter==1.0",
"WhiteNoise==6.7.0",
]
[project.urls]
"Bug Tracker" = "https://github.com/proconnect-gouv/oidc2fer/issues/new"
"Changelog" = "https://github.com/proconnect-gouv/oidc2fer/blob/main/CHANGELOG.md"
"Homepage" = "https://github.com/proconnect-gouv/oidc2fer"
"Repository" = "https://github.com/proconnect-gouv/oidc2fer"
"Bug Tracker" = "https://github.com/numerique-gouv/oidc2fer/issues/new"
"Changelog" = "https://github.com/numerique-gouv/oidc2fer/blob/main/CHANGELOG.md"
"Homepage" = "https://github.com/numerique-gouv/oidc2fer"
"Repository" = "https://github.com/numerique-gouv/oidc2fer"
[project.optional-dependencies]
dev = [
+12 -12
View File
@@ -75,26 +75,26 @@ def test_oidc_to_renater_student_not_allowed(page: Page):
expect(page.locator("pre")).to_contain_text('"error":"access_denied"')
def pro_connect_login(page: Page, email):
def agent_connect_login(page: Page, email):
page.goto("https://fsa1v2.integ01.dev-agentconnect.fr/")
page.get_by_role("button", name="Sidentifier avec ProConnect").click()
page.get_by_label("Connexion à AgentConnect").click()
page.get_by_label("Email professionnel").fill(email)
page.get_by_test_id("interaction-connection-button").click()
def pro_connect_to_renater(
def agent_connect_to_renater(
page: Page,
login="enseignant1",
expected_email="georges.grospieds@formation.renater.fr",
expected_given_name="Georges",
expected_usual_name="Grospieds",
):
pro_connect_login(page, email=expected_email)
agent_connect_login(page, email=expected_email)
renater_wayf(page)
renater_test_idp(page, login=login)
expect(page.locator("body")).to_contain_text(expected_email)
text = page.inner_text("#userinfo")
text = page.inner_text("#json")
result = json.loads(text)
assert {
"uid": f"{login}@test-renater.fr",
@@ -106,22 +106,22 @@ def pro_connect_to_renater(
@pytest.mark.skipif(
"TEST_E2E_PC" not in os.environ, reason="Depends on staging deployment"
"TEST_E2E_AC" not in os.environ, reason="Depends on staging deployment"
)
def test_pro_connect_to_renater_keeps_sub(browser: Browser):
def test_agent_connect_to_renater_keeps_sub(browser: Browser):
with browser.new_context().new_page() as page:
result1 = pro_connect_to_renater(page)
result1 = agent_connect_to_renater(page)
with browser.new_context().new_page() as page:
result2 = pro_connect_to_renater(page)
result2 = agent_connect_to_renater(page)
assert result1["sub"] == result2["sub"]
@pytest.mark.skipif(
"TEST_E2E_PC" not in os.environ, reason="Depends on staging deployment"
"TEST_E2E_AC" not in os.environ, reason="Depends on staging deployment"
)
def test_pro_connect_to_renater_student_not_allowed(page: Page):
pro_connect_login(page, email="jean.dupont@formation.renater.fr")
def test_agent_connect_to_renater_student_not_allowed(page: Page):
agent_connect_login(page, email="jean.dupont@formation.renater.fr")
renater_wayf(page)
renater_test_idp(page, login="etudiant1")