Skip to content
This repository has been archived by the owner on May 13, 2024. It is now read-only.

Latest commit

 

History

History
455 lines (365 loc) · 16.9 KB

deploying.md

File metadata and controls

455 lines (365 loc) · 16.9 KB

This is a step by step guide for deploying sill.code.gouv.fr

The Git based Database

Context

The data are stored in a git based database.

  • the main branch: Each .json file represent a table like in any relational database.
  • the compiled-data branch: Contains two .json files. The first one, compiledData_private.json contains all the information that we have in our database and also the ones collected from different sources like Wikidata or LeComptoirDuLibre, compiled into a single file. This file contains emails of agents and should not be shared publicly. compiledData_public.json on the other end cand be shared, its the same file minus the personal infos about the agents.

There is a bidirectional relationship betwen the Web App and the Data repo, when you update the data repo it updates the web App and the other way around is true as well.

The scrapping and update of the build branch is performed once every four hour and whenever there is a commit on the main branch.

Instantiating a new database instance

  • Click on the green button "use this template" and call it sill-data
  • Check "Include all branches"

Provisioning a Kubernetes cluster

Currently we use the SSPCloud to deploy the SILL but if you have to deploy from scratch here is how to provision and setup a Kubernetes cluster on a cloud provider.

Hashicorp maintains great tutorials for terraforming Kubernetes clusters on AWS, GCP or Azure.

Pick one of the three and follow the guide.

You can stop after the configure kubectl section.

Installing an ingress controller

Deploy an ingress controller on your cluster:

⚠️

The following command is for AWS.

For GCP use this command.

For Azure use this command.

kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.2.0/deploy/static/provider/aws/deploy.yaml

Setting up the DSN

Now you need to get the external address of your cluster, run the command

kubectl get services -n ingress-nginx

and write down the External IP assigned to the LoadBalancer.

Depending on the cloud provider you are using it can be an IPv4, an IPv6 or a domain. On AWS for example, it will be a domain like xxx.elb.eu-west-1.amazonaws.com.

If you see <pending>, wait a few seconds and try again.

Once you have the address, create the following DNS records:

sill.code.gouv.fr. CNAME xxx.elb.eu-west-1.amazonaws.com.
auth.code.gouv.net CNAME xxx.elb.eu-west-1.amazonaws.com.

If the address you got was an IPv4 (x.x.x.x), create a A record instead of a CNAME.

If the address you got was ans IPv6 (y:y:y:y:y:y:y:y), create a AAAA record.

Creating a namespace for our apps

kubectl create namespace projet-codegouv

SSL

https://github.com/codegouvfr/paris-sspcloud/tree/main/cert-manager

Installing Keycloak

UPDATE: Note in this guide we give the instructions for installing Keycloak version 18.
Today it's quite outdated, the latest version ok Keycloak at the time of writing this lines is 23. I won't update this instruction by fear of making an error if I don't test thoroughly.
You can however refer to the installation guide of Onyxia and adapt the instruction for the SILL: https://docs.onyxia.sh/admin-doc/readme/user-authentication
If you install Keycloak 23 or up don't forget to use keycloak-theme.jar instead of retrocompat-keycloak-theme.jar (See note below)

helm repo add codecentric https://codecentric.github.io/helm-charts

POSTGRESQL_PASSWORD=xxxxx #Replace by a strong password, you will never need it.
# Credentials for logging to https://auth.code.gouv.fr/auth
KEYCLOAK_PASSWORD=yyyyyy 

cat << EOF > ./keycloak-values.yaml
image:
  tag: "18.0.2-legacy"
replicas: 1

nodeSelector:
  infra: "true"
tolerations:
  - key: "infra"
    operator: "Exists"

serviceAccount:
  create: true
  name: ""
  annotations: {}
  labels: {}
  imagePullSecrets: []


podSecurityContext:
  fsGroup: 1000


securityContext:
  runAsUser: 1000
  runAsNonRoot: true


extraInitContainers: |
  - name: realm-ext-provider
    image: curlimages/curl
    imagePullPolicy: IfNotPresent
    command:
      - sh
    args:
      - -c
      - |
        curl -L -f -S -o /extensions/keycloak-franceconnect-4.2.0.jar https://github.com/InseeFr/Keycloak-FranceConnect/releases/download/4.2.0/keycloak-franceconnect-4.2.0.jar
        # Here be carefull, the version number should match the version of sill-web in production.
        # Also note that if you are deploying Keycloak 23 or later you should remove the "retrocompat-" in the url below. Keycloak 22 is not supported.  
        curl -L -f -S -o /extensions/sill-web.jar https://github.com/codegouvfr/sill-web/releases/download/v1.40.3/retrocompat-keycloak-theme.jar
    volumeMounts:
      - name: extensions
        mountPath: /extensions

extraVolumeMounts: |
  - name: extensions
    mountPath: /opt/jboss/keycloak/standalone/deployments

extraVolumes: |
  - name: extensions
    emptyDir: {}

# Additional environment variables for Keycloak
extraEnv: |
  - name: KEYCLOAK_USER
    value: admin
  - name: KEYCLOAK_PASSWORD
    value: $KEYCLOAK_PASSWORD
  - name: JGROUPS_DISCOVERY_PROTOCOL
    value: kubernetes.KUBE_PING
  - name: KUBERNETES_NAMESPACE
    valueFrom:
     fieldRef:
       apiVersion: v1
       fieldPath: metadata.namespace
  - name: KEYCLOAK_STATISTICS
    value: "true"
  - name: CACHE_OWNERS_COUNT
    value: "2"
  - name: CACHE_OWNERS_AUTH_SESSIONS_COUNT
    value: "2"
  - name: PROXY_ADDRESS_FORWARDING
    value: "true"
  - name: JAVA_OPTS
    value: >-
      -XX:+UseContainerSupport -XX:MaxRAMPercentage=50.0        -Djava.net.preferIPv4Stack=true        -Djboss.modules.system.pkgs=$JBOSS_MODULES_SYSTEM_PKGS   -Djava.awt.headless=true       -Dkeycloak.profile=preview

service:
  labels: {}
  type: ClusterIP
  loadBalancerIP: ""
  httpPort: 80
  httpNodePort: null
  httpsPort: 8443
  httpsNodePort: null
  httpManagementPort: 9990
  httpManagementNodePort: null
  extraPorts: []
  loadBalancerSourceRanges: []
  sessionAffinity: ""
  sessionAffinityConfig: {}

ingress:
  enabled: true
  servicePort: http
  annotations:
    nginx.ingress.kubernetes.io/proxy-buffer-size: 128k

  labels: {}
  rules:
    - host: "tmp-auth-codegouv.lab.sspcloud.fr"
      paths:
        - path: /
          pathType: Prefix
  tls:
    - hosts:
        - tmp-auth-codegouv.lab.sspcloud.fr
      secretName: sill-tls

networkPolicy:
  enabled: false
  labels: {}
  extraFrom: []

postgresql:
  enabled: true
  postgresqlUsername: keycloak
  postgresqlPassword: "$POSTGRESQL_PASSWORD"
  postgresqlDatabase: keycloak
  networkPolicy:
    enabled: false

EOF

helm install keycloak codecentric/keycloak -f keycloak-values.yaml -n projet-sill

You can now login to the administration console of https://sill-auth.my-domain.net and login using the credentials you have defined with KEYCLOAK_USER and KEYCLOAK_PASSWORD.

  1. Create a realm called "codegouv" (or something else), go to Realm settings
    1. On the tab General
      1. User Profile Enabled: On
    2. On the tab login
      1. User registration: On
      2. Forgot password: On
      3. Remember me: On
      4. Verify email: On
    3. On the tab email, we give an example with **** AWS SES, if you don't have a SMTP server at hand you can skip this by going to Authentication (on the left panel) -> Tab Required Actions -> Uncheck "set as default action" Verify Email. Be aware that with email verification disable, anyone will be able to sign up to your service.
      1. From: noreply@lab.my-domain.net
      2. Host: email-smtp.us-east-2.amazonaws.com
      3. Port: 465
      4. Authentication: enabled
      5. Username: **************
      6. Password: ***************************************
      7. When clicking "save" you'll be asked for a test email, you have to provide one that correspond to a pre-existing user or you will get a silent error and the credentials won't be saved.
    4. On the tab Themes
      1. Login theme: dsfr (you can also select the login theme on a per client basis)
      2. Email theme: dsfr
      3. Internationalization: Enabled
      4. Supported locales: en fr
    5. On the tab Session
      1. SSO Session Idle: 14 days - This setting and the following two are so that when the user click "remember me" when he logs in, he dosen't have to loggin again for the next two weeks.
      2. SSO Session Idle Remember Me: 14 days
      3. SSO Session Max Remember Me: 14 days
  2. Create a client called "sill"
    1. Root URL: https://sill.code.gouv.fr/
    2. Valid redirect URIs: https://sill.code.gouv.fr/\ ( and http://localhost\ for developpement)**
    3. Web origins: *
  3. In Authentication (on the left panel) -> Tab Required Actions enable and set as default action Therms and Conditions.

Now you want to define a list of email domain allowed to register to your service.

Go to Realm Settings (on the left panel) -> Tab User Profile (this tab shows up only if User Profile is enabled in the General tab and you can enable user profile only if you have started Keycloak with -Dkeycloak.profile=preview) -> JSON Editor.

Now you can edit the file as suggested in the following DIFF snippet. Be mindful that in this example we only allow emails @gmail.com and @hotmail.com to register you want to edit that.

{
   "attributes": [
     {
       "name": "email",
       "displayName": "${email}",
       "validations": {
         "email": {},
         "length": {
           "max": 255
         },
+       "pattern": {
+         "pattern": "^[^@]+@([^.]+\\.)*((gmail\\.com)|(hotmail\\.com))$"
+      }
       },
       "permissions": {
         "view": [],
         "edit": []
       },
       "selector": {
         "scopes": []
       }
     },
+    {
+      "selector": {
+        "scopes": []
+      },
+      "permissions": {
+        "view": [
+          "user",
+          "admin"
+        ],
+        "edit": [
+          "user",
+          "admin"
+        ]
+      },
+      "name": "agencyName",
+      "displayName": "${organization}",
+      "validations": {},
+      "required": {
+        "roles": [
+          "user"
+        ],
+        "scopes": []
+      }
+   }
   ]
 }

ℹ️

We create the User Profile Attribute "agencyName" instead of organization because of legacy reasons. Our database of user use this name and we can't migrate.

Finally you need to create mapper so that agencyName appears in the JWT.

Go to clients -> sill -> Mappers

  • Name: organization
  • Mapper type: User attribute
  • User attribute: agencyName
  • Token claim name: organization
  • Claim JSON type: string

Enabling AgentConnect

To enable agent connect you need to use this extention (I's already loaded in your Keycloak if you look carefully in your keycloak-values.yaml file. )

Follow the instructions in the readme of InseeFr/Keycloak-FranceConnect.

Theses are the information that you'll need to give to the France Connect team to receive your credentials:

Nom du fournisseur de service: "Socle interministériel de logiciels libres" ( ou SILL pour les intimes )
URL de redirection de connexion: https://sill-auth.my-domain.net/auth/realms/codegouv/broker/agentconnect/endpoint
URL de redirection de déconnexion: https://sill-auth.my-domain.net/auth/realms/codegouv/broker/agentconnect/endpoint/logout_response
URL du bouton affichant AgentConnect: Le bouton AgentConnect sera affiché sur la page le login, on accède a la page de login en cliquant sur "Connexion" en haut à droite de l'écran sur https://sill.code.gouv.fr/
Scopes désirés: given_name usual_name email organizational_unit
Algorithme de la signature des userinfos: ES256
Algorithme de la signature de l'id token: ES256

You'll also need to create a mapper for organizational_unit -> agencyName.

To do so, go to Identity Providers -> Agent Connect -> Mappers -> create

Instantiating the web app

First of all you need to enable SSH autentication via private/public key on GitHub (or whatever platfrom you're using):

  • Generate a priv/pub key if you don't have one already: ssh-keygen -o -a 100 -t ed25519 -f ~/.ssh/id_ed25519 -C "john@example.com"
  • Got to your global GitHub setting, then SSH and GPG Keys, new SSH Key and pass the content of ~/.ssh/id_ed25519.pub.
helm repo add codegouvfr https://codegouvfr.github.io/helm-charts
SSH_PRIVATE_KEY_NAME=id_ed25519 # For example, generated earlyer
SSH_PRIVATE_KEY="-----BEGIN OPENSSH PRIVATE KEY-----\nxxxx\nxxxx\nxxxx\nAxxxx\nxxxx\n-----END OPENSSH PRIVATE KEY-----\n"
DATA_REPO_SSH_URL="git@github.com:codegouvfr/sill-data.git" # Replace by the repo you created earlyer
KEYCLOAK_PASSWORD=yyyyyy # Make sure it's the same that the one you defined earlyer
WEBHOOK_SECRET=dSdSPxxxxxx # (optional) Some random caracters 
GITHUB_TOKEN=ghp_xxxxxx # A token, just for the GitHub API rate limit.  
SOFTWARE_DATA_ORIGIN=wikidata # Can be "wikidata" or "HAL" (See: https://hal.science/)

WEB_VERSION="1.41.2" # To keep up to date with https://github.com/codegouvfr/sill-web/releases/
API_VERSION="1.20.5" # To keep up to date with https://github.com/codegouvfr/sill-api/releases/

cat << EOF > ./sill-values.yaml
ingress:
  enabled: true
  annotations:
    kubernetes.io/ingress.class: nginx
  hosts:
    - host: sill.code.gouv.fr
  tls:
    - hosts:
        - sill.code.gouv.fr
web:
  replicaCount: 2
  image:
    version: $WEB_VERSION
  nodeSelector:
    infra: "true"
  tolerations:
    - key: "infra"
      operator: "Exists"
  
api:
  replicaCount: 1
  image:
    version: $API_VERSION
  nodeSelector:
    infra: "true"
  tolerations:
    - key: "infra"
      operator: "Exists"
  env:
    # For seeing all what's available: https://github.com/codegouvfr/sill-api/blob/main/src/env.ts
    CONFIGURATION: |
      {
        "keycloakParams": {
          "url": "https://auth.code.gouv.fr/auth",
          "realm": "codegouv",
          "clientId": "sill",
          "adminPassword": "$KEYCLOAK_PASSWORD",
          "organizationUserProfileAttributeName": "agencyName"
        },
        "termsOfServiceUrl": "https://sill-preprod.lab.sspcloud.fr/tos_fr.md",
        "readmeUrl": "https://git.sr.ht/~codegouvfr/logiciels-libres/blob/main/sill.md",
        "jwtClaimByUserKey": {
          "id": "sub",
          "email": "email",
          "organization": "organization"
        },
        "dataRepoSshUrl": "git@github.com:codegouvfr/sill-data-test.git",
        "sshPrivateKeyForGitName": "$SSH_PRIVATE_KEY_NAME",
        "sshPrivateKeyForGit": "$SSH_PRIVATE_KEY",
        "githubWebhookSecret": "$WEBHOOK_SECRET",
        "githubPersonalAccessTokenForApiRateLimit": "$GITHUB_TOKEN",
        "externalSoftwareDataOrigin": "$SOFTWARE_DATA_ORIGIN"
      }

EOF

helm install sill codegouvfr/sill -f sill-values.yaml -n projet-sill

Enabling webhooks

By default the web app periodically checks the data repo for update.

If you want, and if you data repo is hosted on GitHub you can enable a Webhook that will ping the web app whenever there is an update.

Type some random string as secret. You then need to provide it to sill-api so it know it can trust the ping to be genuine (you can do that later, for now just write down the secret).