<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://wiki.csclub.uwaterloo.ca/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=K95ma</id>
	<title>CSCWiki - User contributions [en]</title>
	<link rel="self" type="application/atom+xml" href="https://wiki.csclub.uwaterloo.ca/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=K95ma"/>
	<link rel="alternate" type="text/html" href="https://wiki.csclub.uwaterloo.ca/Special:Contributions/K95ma"/>
	<updated>2026-04-05T22:52:34Z</updated>
	<subtitle>User contributions</subtitle>
	<generator>MediaWiki 1.44.0</generator>
	<entry>
		<id>https://wiki.csclub.uwaterloo.ca/index.php?title=Matrix&amp;diff=5453</id>
		<title>Matrix</title>
		<link rel="alternate" type="text/html" href="https://wiki.csclub.uwaterloo.ca/index.php?title=Matrix&amp;diff=5453"/>
		<updated>2025-10-29T02:00:38Z</updated>

		<summary type="html">&lt;p&gt;K95ma: /* Discord Instructions */ fix&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;We are currently setting up a test server for Matrix. We use Synapse. If everything goes well, we will set up a production Matrix server.&lt;br /&gt;
&lt;br /&gt;
== Server Setup ==&lt;br /&gt;
&lt;br /&gt;
We are currently running Matrix on a Proxmox LXE container on &amp;lt;code&amp;gt;citric-acid&amp;lt;/code&amp;gt;. Ask Siracha for the credentials to access Proxmox and the VM.&lt;br /&gt;
&lt;br /&gt;
== Matrix Installation ==&lt;br /&gt;
&lt;br /&gt;
We are using NixOS, so the config to setup both Synapse and PostgreSQL is hosted at https://git.csclub.uwaterloo.ca/k95ma/matrix-nixos/.&lt;br /&gt;
&lt;br /&gt;
The reverse proxy, Caddy is configured on citric-acid.&lt;br /&gt;
&lt;br /&gt;
== Testing ==&lt;br /&gt;
&lt;br /&gt;
Go to https://app.cinny.in/login/csclub.uwaterloo.ca, and click &amp;quot;Continue with CSC&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
== Bridging ==&lt;br /&gt;
&lt;br /&gt;
We are currently running mautrix-discord. Bridge to IRC via heisenbridge may be added in the future.&lt;br /&gt;
&lt;br /&gt;
=== Discord Instructions ===&lt;br /&gt;
&lt;br /&gt;
* Create the Matrix channel to bridge to&lt;br /&gt;
* Invite {{code|@beefbot:csclub.uwaterloo.ca}} and {{code|@discordbot:csclub.uwaterloo.ca}} to the channel&lt;br /&gt;
* Login to User {{code|beefbot}} (ask syscom for password) on our matrix instance (when we eventually disable password login, you will need to edit the nix config to re-enable it)&lt;br /&gt;
* &#039;&#039;Inside&#039;&#039; the matrix channel, send, {{code|!discord bridge &amp;lt;channel ID&amp;gt;}} (the channel ID on Discord, the Discord bot must be able to see that channel)&lt;br /&gt;
*  Create a Discord Webhook URL for that channel on Discord&#039;s side, and then DM the discord bot with {{code|!discord set-relay !26DmcJd3cQ...:csclub.uwaterloo.ca --url https://discord.com/...}} with the first argument replaced by the actual room identifier and the second parameter the webhook URL.&lt;/div&gt;</summary>
		<author><name>K95ma</name></author>
	</entry>
	<entry>
		<id>https://wiki.csclub.uwaterloo.ca/index.php?title=Matrix&amp;diff=5452</id>
		<title>Matrix</title>
		<link rel="alternate" type="text/html" href="https://wiki.csclub.uwaterloo.ca/index.php?title=Matrix&amp;diff=5452"/>
		<updated>2025-10-29T01:57:23Z</updated>

		<summary type="html">&lt;p&gt;K95ma: /* Discord Instructions */ italic&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;We are currently setting up a test server for Matrix. We use Synapse. If everything goes well, we will set up a production Matrix server.&lt;br /&gt;
&lt;br /&gt;
== Server Setup ==&lt;br /&gt;
&lt;br /&gt;
We are currently running Matrix on a Proxmox LXE container on &amp;lt;code&amp;gt;citric-acid&amp;lt;/code&amp;gt;. Ask Siracha for the credentials to access Proxmox and the VM.&lt;br /&gt;
&lt;br /&gt;
== Matrix Installation ==&lt;br /&gt;
&lt;br /&gt;
We are using NixOS, so the config to setup both Synapse and PostgreSQL is hosted at https://git.csclub.uwaterloo.ca/k95ma/matrix-nixos/.&lt;br /&gt;
&lt;br /&gt;
The reverse proxy, Caddy is configured on citric-acid.&lt;br /&gt;
&lt;br /&gt;
== Testing ==&lt;br /&gt;
&lt;br /&gt;
Go to https://app.cinny.in/login/csclub.uwaterloo.ca, and click &amp;quot;Continue with CSC&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
== Bridging ==&lt;br /&gt;
&lt;br /&gt;
We are currently running mautrix-discord. Bridge to IRC via heisenbridge may be added in the future.&lt;br /&gt;
&lt;br /&gt;
=== Discord Instructions ===&lt;br /&gt;
&lt;br /&gt;
* Create the Matrix channel to bridge to&lt;br /&gt;
* Invite {{code|@beefbot:csclub.uwaterloo.ca}} and {{code|@discordbot:csclub.uwaterloo.ca}} to the channel&lt;br /&gt;
* Login to User {{code|beefbot}} (ask syscom for password) on our matrix instance (when we eventually disable password login, you will need to edit the nix config to re-enable it)&lt;br /&gt;
*  Open the DM with {{code|@discordbot:csclub.uwaterloo.ca}} on beefbot&#039;s account, &#039;&#039;inside&#039;&#039; the matrix channel, send, {{code|!discord bridge &amp;lt;channel ID&amp;gt;}} (the channel ID on Discord, the Discord bot must be able to see that channel)&lt;br /&gt;
*  Create a Discord Webhook URL for that channel on Discord&#039;s side, and then DM the discord bot with {{code|!discord set-relay !26DmcJd3cQ...:csclub.uwaterloo.ca --url https://discord.com/...}} with the first argument replaced by the actual room identifier and the second parameter the webhook URL.&lt;/div&gt;</summary>
		<author><name>K95ma</name></author>
	</entry>
	<entry>
		<id>https://wiki.csclub.uwaterloo.ca/index.php?title=Matrix&amp;diff=5451</id>
		<title>Matrix</title>
		<link rel="alternate" type="text/html" href="https://wiki.csclub.uwaterloo.ca/index.php?title=Matrix&amp;diff=5451"/>
		<updated>2025-10-29T01:56:15Z</updated>

		<summary type="html">&lt;p&gt;K95ma: /* Discord Instructions */ fix&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;We are currently setting up a test server for Matrix. We use Synapse. If everything goes well, we will set up a production Matrix server.&lt;br /&gt;
&lt;br /&gt;
== Server Setup ==&lt;br /&gt;
&lt;br /&gt;
We are currently running Matrix on a Proxmox LXE container on &amp;lt;code&amp;gt;citric-acid&amp;lt;/code&amp;gt;. Ask Siracha for the credentials to access Proxmox and the VM.&lt;br /&gt;
&lt;br /&gt;
== Matrix Installation ==&lt;br /&gt;
&lt;br /&gt;
We are using NixOS, so the config to setup both Synapse and PostgreSQL is hosted at https://git.csclub.uwaterloo.ca/k95ma/matrix-nixos/.&lt;br /&gt;
&lt;br /&gt;
The reverse proxy, Caddy is configured on citric-acid.&lt;br /&gt;
&lt;br /&gt;
== Testing ==&lt;br /&gt;
&lt;br /&gt;
Go to https://app.cinny.in/login/csclub.uwaterloo.ca, and click &amp;quot;Continue with CSC&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
== Bridging ==&lt;br /&gt;
&lt;br /&gt;
We are currently running mautrix-discord. Bridge to IRC via heisenbridge may be added in the future.&lt;br /&gt;
&lt;br /&gt;
=== Discord Instructions ===&lt;br /&gt;
&lt;br /&gt;
* Create the Matrix channel to bridge to&lt;br /&gt;
* Invite {{code|@beefbot:csclub.uwaterloo.ca}} and {{code|@discordbot:csclub.uwaterloo.ca}} to the channel&lt;br /&gt;
* Login to User {{code|beefbot}} (ask syscom for password) on our matrix instance (when we eventually disable password login, you will need to edit the nix config to re-enable it)&lt;br /&gt;
*  Open the DM with {{code|@discordbot:csclub.uwaterloo.ca}} on beefbot&#039;s account, _inside_ the matrix channel, send, {{code|!discord bridge &amp;lt;channel ID&amp;gt;}} (the channel ID on Discord, the Discord bot must be able to see that channel)&lt;br /&gt;
*  Create a Discord Webhook URL for that channel on Discord&#039;s side, and then DM the discord bot with {{code|!discord set-relay !26DmcJd3cQ...:csclub.uwaterloo.ca --url https://discord.com/...}} with the first argument replaced by the actual room identifier and the second parameter the webhook URL.&lt;/div&gt;</summary>
		<author><name>K95ma</name></author>
	</entry>
	<entry>
		<id>https://wiki.csclub.uwaterloo.ca/index.php?title=Matrix&amp;diff=5450</id>
		<title>Matrix</title>
		<link rel="alternate" type="text/html" href="https://wiki.csclub.uwaterloo.ca/index.php?title=Matrix&amp;diff=5450"/>
		<updated>2025-10-29T01:55:56Z</updated>

		<summary type="html">&lt;p&gt;K95ma: /* Discord Instructions */ +&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;We are currently setting up a test server for Matrix. We use Synapse. If everything goes well, we will set up a production Matrix server.&lt;br /&gt;
&lt;br /&gt;
== Server Setup ==&lt;br /&gt;
&lt;br /&gt;
We are currently running Matrix on a Proxmox LXE container on &amp;lt;code&amp;gt;citric-acid&amp;lt;/code&amp;gt;. Ask Siracha for the credentials to access Proxmox and the VM.&lt;br /&gt;
&lt;br /&gt;
== Matrix Installation ==&lt;br /&gt;
&lt;br /&gt;
We are using NixOS, so the config to setup both Synapse and PostgreSQL is hosted at https://git.csclub.uwaterloo.ca/k95ma/matrix-nixos/.&lt;br /&gt;
&lt;br /&gt;
The reverse proxy, Caddy is configured on citric-acid.&lt;br /&gt;
&lt;br /&gt;
== Testing ==&lt;br /&gt;
&lt;br /&gt;
Go to https://app.cinny.in/login/csclub.uwaterloo.ca, and click &amp;quot;Continue with CSC&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
== Bridging ==&lt;br /&gt;
&lt;br /&gt;
We are currently running mautrix-discord. Bridge to IRC via heisenbridge may be added in the future.&lt;br /&gt;
&lt;br /&gt;
=== Discord Instructions ===&lt;br /&gt;
&lt;br /&gt;
* Create the Matrix channel to bridge to&lt;br /&gt;
* Invite {{code|@beefbot:csclub.uwaterloo.ca}} and {{code|@discordbot:csclub.uwaterloo.ca}} to the channel&lt;br /&gt;
* Login to User {{code|beefbot}} (ask syscom for password) on our matrix instance (when we eventually disable password login, you will need to edit the nix config to re-enable it)&lt;br /&gt;
*  Open the DM with {{code|@discordbot:csclub.uwaterloo.ca  on beefbot&#039;s account, _inside_ the matrix channel, send, {{code|!discord bridge &amp;lt;channel ID&amp;gt;}} (the channel ID on Discord, the Discord bot must be able to see that channel)&lt;br /&gt;
*  Create a Discord Webhook URL for that channel on Discord&#039;s side, and then DM the discord bot with {{code|!discord set-relay !26DmcJd3cQ...:csclub.uwaterloo.ca --url https://discord.com/...}} with the first argument replaced by the actual room identifier and the second parameter the webhook URL.&lt;/div&gt;</summary>
		<author><name>K95ma</name></author>
	</entry>
	<entry>
		<id>https://wiki.csclub.uwaterloo.ca/index.php?title=Template:Code&amp;diff=5449</id>
		<title>Template:Code</title>
		<link rel="alternate" type="text/html" href="https://wiki.csclub.uwaterloo.ca/index.php?title=Template:Code&amp;diff=5449"/>
		<updated>2025-10-29T01:55:11Z</updated>

		<summary type="html">&lt;p&gt;K95ma: copied template from https://en.wikipedia.org/w/index.php?title=Template:Code&amp;amp;action=history; see url for attribution&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{#tag:syntaxhighlight|{{{code|{{{1}}}}}}|lang={{{lang|{{{2|text}}}}}}|class={{{class|}}}|style={{{style|}}}|inline=1}}&lt;/div&gt;</summary>
		<author><name>K95ma</name></author>
	</entry>
	<entry>
		<id>https://wiki.csclub.uwaterloo.ca/index.php?title=Matrix&amp;diff=5448</id>
		<title>Matrix</title>
		<link rel="alternate" type="text/html" href="https://wiki.csclub.uwaterloo.ca/index.php?title=Matrix&amp;diff=5448"/>
		<updated>2025-10-29T01:53:31Z</updated>

		<summary type="html">&lt;p&gt;K95ma: +&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;We are currently setting up a test server for Matrix. We use Synapse. If everything goes well, we will set up a production Matrix server.&lt;br /&gt;
&lt;br /&gt;
== Server Setup ==&lt;br /&gt;
&lt;br /&gt;
We are currently running Matrix on a Proxmox LXE container on &amp;lt;code&amp;gt;citric-acid&amp;lt;/code&amp;gt;. Ask Siracha for the credentials to access Proxmox and the VM.&lt;br /&gt;
&lt;br /&gt;
== Matrix Installation ==&lt;br /&gt;
&lt;br /&gt;
We are using NixOS, so the config to setup both Synapse and PostgreSQL is hosted at https://git.csclub.uwaterloo.ca/k95ma/matrix-nixos/.&lt;br /&gt;
&lt;br /&gt;
The reverse proxy, Caddy is configured on citric-acid.&lt;br /&gt;
&lt;br /&gt;
== Testing ==&lt;br /&gt;
&lt;br /&gt;
Go to https://app.cinny.in/login/csclub.uwaterloo.ca, and click &amp;quot;Continue with CSC&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
== Bridging ==&lt;br /&gt;
&lt;br /&gt;
We are currently running mautrix-discord. Bridge to IRC via heisenbridge may be added in the future.&lt;br /&gt;
&lt;br /&gt;
=== Discord Instructions ===&lt;br /&gt;
&lt;br /&gt;
* Create the Matrix channel to bridge to&lt;br /&gt;
* Invite @beefbot:csclub.uwaterloo.ca and @discordbot:csclub.uwaterloo.ca  to the channel&lt;br /&gt;
* Login to User `beefbot` (ask syscom for password) on our matrix instance (when we eventually disable password login, you will need to edit the nix config to re-enable it)&lt;br /&gt;
*  Open the DM with @discordbot:csclub.uwaterloo.ca  on beefbot&#039;s account, _inside_ the matrix channel, send, `!discord bridge &amp;lt;channel ID&amp;gt;` (the channel ID on Discord, the Discord bot must be able to see that channel)&lt;br /&gt;
*  Create a Discord Webhook URL for that channel on Discord&#039;s side, and then DM the discord bot with `!discord set-relay !26DmcJd3cQ...:csclub.uwaterloo.ca --url https://discord.com/...` with the first argument replaced by the actual room identifier and the second parameter the webhook URL.&lt;/div&gt;</summary>
		<author><name>K95ma</name></author>
	</entry>
	<entry>
		<id>https://wiki.csclub.uwaterloo.ca/index.php?title=Matrix&amp;diff=5447</id>
		<title>Matrix</title>
		<link rel="alternate" type="text/html" href="https://wiki.csclub.uwaterloo.ca/index.php?title=Matrix&amp;diff=5447"/>
		<updated>2025-10-26T21:18:23Z</updated>

		<summary type="html">&lt;p&gt;K95ma: /* Matrix Installation */ -&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;We are currently setting up a test server for Matrix. We use Synapse. If everything goes well, we will set up a production Matrix server.&lt;br /&gt;
&lt;br /&gt;
== Server Setup ==&lt;br /&gt;
&lt;br /&gt;
We are currently running Matrix on a Proxmox LXE container on &amp;lt;code&amp;gt;citric-acid&amp;lt;/code&amp;gt;. Ask Siracha for the credentials to access Proxmox and the VM.&lt;br /&gt;
&lt;br /&gt;
== Matrix Installation ==&lt;br /&gt;
&lt;br /&gt;
We are using NixOS, so the config to setup both Synapse and PostgreSQL is hosted at https://git.csclub.uwaterloo.ca/k95ma/matrix-nixos/.&lt;br /&gt;
&lt;br /&gt;
The reverse proxy, Caddy is configured on citric-acid.&lt;br /&gt;
&lt;br /&gt;
== Testing ==&lt;br /&gt;
&lt;br /&gt;
Go to https://app.cinny.in/login/csclub.uwaterloo.ca, and click &amp;quot;Continue with CSC&amp;quot;.&lt;/div&gt;</summary>
		<author><name>K95ma</name></author>
	</entry>
	<entry>
		<id>https://wiki.csclub.uwaterloo.ca/index.php?title=Matrix&amp;diff=5446</id>
		<title>Matrix</title>
		<link rel="alternate" type="text/html" href="https://wiki.csclub.uwaterloo.ca/index.php?title=Matrix&amp;diff=5446"/>
		<updated>2025-10-26T20:59:44Z</updated>

		<summary type="html">&lt;p&gt;K95ma: +&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;We are currently setting up a test server for Matrix. We use Synapse. If everything goes well, we will set up a production Matrix server.&lt;br /&gt;
&lt;br /&gt;
== Server Setup ==&lt;br /&gt;
&lt;br /&gt;
We are currently running Matrix on a Proxmox LXE container on &amp;lt;code&amp;gt;citric-acid&amp;lt;/code&amp;gt;. Ask Siracha for the credentials to access Proxmox and the VM.&lt;br /&gt;
&lt;br /&gt;
== Matrix Installation ==&lt;br /&gt;
&lt;br /&gt;
We are using NixOS, so the config to setup both Synapse and PostgreSQL is hosted at https://git.csclub.uwaterloo.ca/k95ma/matrix-nixos/.&lt;br /&gt;
&lt;br /&gt;
The reverse proxy, Caddy is configured on citric-acid.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Testing ==&lt;br /&gt;
&lt;br /&gt;
Go to https://app.cinny.in/login/csclub.uwaterloo.ca, and click &amp;quot;Continue with CSC&amp;quot;.&lt;/div&gt;</summary>
		<author><name>K95ma</name></author>
	</entry>
	<entry>
		<id>https://wiki.csclub.uwaterloo.ca/index.php?title=NixOS&amp;diff=5429</id>
		<title>NixOS</title>
		<link rel="alternate" type="text/html" href="https://wiki.csclub.uwaterloo.ca/index.php?title=NixOS&amp;diff=5429"/>
		<updated>2025-09-29T04:23:38Z</updated>

		<summary type="html">&lt;p&gt;K95ma: +&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;We&#039;re trying to explore different options for running services, and &#039;&#039;&#039;NixOS on Proxmox containers&#039;&#039;&#039; is one of them. Here&#039;s how it is supposed to work:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Malleable&#039;&#039;&#039;: it should be relatively easy to modify an existing service config, update software version, and migrate one software to another, as everything are written in Nix configuration files.&lt;br /&gt;
* &#039;&#039;&#039;Recoverable&#039;&#039;&#039;: NixOS keeps old copies of the system, which can be reverted if we see any immediate issues.&lt;br /&gt;
* &#039;&#039;&#039;Discoverable&#039;&#039;&#039;: Services are located in one canonical, centralized location. This reduces the time needed to find the specific config for a specific software, and also makes it easy for someone to know what services are running.&lt;br /&gt;
* &#039;&#039;&#039;Replicable&#039;&#039;&#039;: As NixOS service configuration files are written in a human readable format, anyone wishing to use the &amp;quot;normal&amp;quot; way to configure their service should be able to understand how to setup their service in a similar way.&lt;br /&gt;
* &#039;&#039;&#039;Trackable&#039;&#039;&#039;: Easy to manage and track changes using Git, maybe even with CI.&lt;br /&gt;
&lt;br /&gt;
=== Setting up a Proxmox VM ===&lt;br /&gt;
&lt;br /&gt;
If there isn&#039;t a template already, use https://hydra.nixos.org/job/nixos/release-25.05/nixos.proxmoxLXC.x86_64-linux (replace 25.05 with the latest release)&lt;br /&gt;
&lt;br /&gt;
Use &amp;quot;Create CT&amp;quot;, do not use the SSH config, and setup a root password. Then use the console on the web interface to login as root (press enter if you see a blank screen) and configure SSH from there.&lt;/div&gt;</summary>
		<author><name>K95ma</name></author>
	</entry>
	<entry>
		<id>https://wiki.csclub.uwaterloo.ca/index.php?title=Matrix&amp;diff=5427</id>
		<title>Matrix</title>
		<link rel="alternate" type="text/html" href="https://wiki.csclub.uwaterloo.ca/index.php?title=Matrix&amp;diff=5427"/>
		<updated>2025-09-18T02:50:09Z</updated>

		<summary type="html">&lt;p&gt;K95ma: +&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;We are currently setting up a test server for Matrix. We use Synapse. If everything goes well, we will set up a production Matrix server.&lt;br /&gt;
&lt;br /&gt;
== Server Setup ==&lt;br /&gt;
&lt;br /&gt;
We are currently running Matrix on a Proxmox LXE container on &amp;lt;code&amp;gt;citric-acid&amp;lt;/code&amp;gt;. Ask Siracha for the credentials to access Proxmox and the VM.&lt;br /&gt;
&lt;br /&gt;
== Matrix Installation ==&lt;br /&gt;
&lt;br /&gt;
We are using NixOS, so use the following config (might be a bit messy) to setup both Synapse, Nginx, and PostgreSQL:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note&#039;&#039;&#039;: We could have used &amp;lt;code&amp;gt;recommendedProxySettings = true&amp;lt;/code&amp;gt; which sets most of the &amp;lt;code&amp;gt;X-Forwarded-For&amp;lt;/code&amp;gt; headers correctly. However, this is a reverse proxy behind a reverse proxy (Nginx on citric acid -&amp;gt; Nginx on the container -&amp;gt; Synapse) so &amp;lt;code&amp;gt;X-Forwarded-Proto&amp;lt;/code&amp;gt; has to be always set to https. I&#039;m sure there is a better way to do this.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{ pkgs, lib, config, ... }:&lt;br /&gt;
let&lt;br /&gt;
  fqdn = &amp;quot;matrix.${config.networking.domain}&amp;quot;;&lt;br /&gt;
  clientConfig = {&lt;br /&gt;
    &amp;quot;m.homeserver&amp;quot;.base_url = &amp;quot;https://${fqdn}&amp;quot;;&lt;br /&gt;
    &amp;quot;m.identity_server&amp;quot; = {};&lt;br /&gt;
  };&lt;br /&gt;
  serverConfig.&amp;quot;m.server&amp;quot; = &amp;quot;${fqdn}:443&amp;quot;;&lt;br /&gt;
  mkWellKnown = data: &#039;&#039;&lt;br /&gt;
    add_header Content-Type application/json;&lt;br /&gt;
    add_header Access-Control-Allow-Origin *;&lt;br /&gt;
    return 200 &#039;${builtins.toJSON data}&#039;;&lt;br /&gt;
  &#039;&#039;;&lt;br /&gt;
  extraCfg = pkgs.writeText &amp;quot;synapse-extra-config.yaml&amp;quot; &#039;&#039;&lt;br /&gt;
  &#039;&#039;;&lt;br /&gt;
in {&lt;br /&gt;
  networking.firewall = {&lt;br /&gt;
    enable = true;&lt;br /&gt;
    allowedTCPPorts = [ 80 8008 ];&lt;br /&gt;
  };&lt;br /&gt;
  networking.domain = &amp;quot;csclub.uwaterloo.ca&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
  services.postgresql = {&lt;br /&gt;
    enable = true;&lt;br /&gt;
    initialScript = pkgs.writeText &amp;quot;synapse-init.sql&amp;quot; &#039;&#039;&lt;br /&gt;
      CREATE ROLE &amp;quot;matrix-synapse&amp;quot; WITH LOGIN PASSWORD &#039;synapse&#039;;&lt;br /&gt;
      CREATE DATABASE &amp;quot;matrix-synapse&amp;quot; WITH OWNER &amp;quot;matrix-synapse&amp;quot;&lt;br /&gt;
        TEMPLATE template0&lt;br /&gt;
        LC_COLLATE = &amp;quot;C&amp;quot;&lt;br /&gt;
        LC_CTYPE = &amp;quot;C&amp;quot;;&lt;br /&gt;
    &#039;&#039;;&lt;br /&gt;
    dataDir = &amp;quot;/data/postgresql&amp;quot;;&lt;br /&gt;
  };&lt;br /&gt;
&lt;br /&gt;
  services.nginx = {&lt;br /&gt;
    enable = true;&lt;br /&gt;
    # recommendedProxySettings = true;&lt;br /&gt;
    virtualHosts = {&lt;br /&gt;
      &amp;quot;csclub.uwaterloo.ca&amp;quot; = {&lt;br /&gt;
        locations.&amp;quot;/&amp;quot;.extraConfig = &#039;&#039;&lt;br /&gt;
          return 404;&lt;br /&gt;
        &#039;&#039;;&lt;br /&gt;
        locations.&amp;quot;= /.well-known/matrix/server&amp;quot;.extraConfig = mkWellKnown serverConfig;&lt;br /&gt;
        locations.&amp;quot;= /.well-known/matrix/client&amp;quot;.extraConfig = mkWellKnown clientConfig;&lt;br /&gt;
      };&lt;br /&gt;
      &amp;quot;${fqdn}&amp;quot; = {&lt;br /&gt;
        locations.&amp;quot;/&amp;quot;.extraConfig = &#039;&#039;&lt;br /&gt;
          return 404;&lt;br /&gt;
        &#039;&#039;;&lt;br /&gt;
        locations.&amp;quot;/_matrix&amp;quot;.proxyPass = &amp;quot;http://[::1]:8008&amp;quot;;&lt;br /&gt;
        locations.&amp;quot;/_matrix&amp;quot;.extraConfig = &#039;&#039;&lt;br /&gt;
    proxy_set_header        Host $host;&lt;br /&gt;
    proxy_set_header        X-Real-IP $remote_addr;&lt;br /&gt;
    proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;&lt;br /&gt;
    proxy_set_header        X-Forwarded-Proto https;&lt;br /&gt;
    proxy_set_header        X-Forwarded-Host $host;&lt;br /&gt;
    proxy_set_header        X-Forwarded-Server $host;&lt;br /&gt;
        &#039;&#039;;&lt;br /&gt;
        locations.&amp;quot;/_synapse/client&amp;quot;.proxyPass = &amp;quot;http://[::1]:8008&amp;quot;;&lt;br /&gt;
        locations.&amp;quot;/_synapse/client&amp;quot;.extraConfig = &#039;&#039;&lt;br /&gt;
    proxy_set_header        Host $host;&lt;br /&gt;
    proxy_set_header        X-Real-IP $remote_addr;&lt;br /&gt;
    proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;&lt;br /&gt;
    proxy_set_header        X-Forwarded-Proto https;&lt;br /&gt;
    proxy_set_header        X-Forwarded-Host $host;&lt;br /&gt;
    proxy_set_header        X-Forwarded-Server $host;&lt;br /&gt;
        &#039;&#039;;&lt;br /&gt;
      };&lt;br /&gt;
    };&lt;br /&gt;
  };&lt;br /&gt;
&lt;br /&gt;
  services.matrix-synapse = {&lt;br /&gt;
    enable = true;&lt;br /&gt;
    extraConfigFiles = [ extraCfg ];&lt;br /&gt;
    dataDir = &amp;quot;/data/matrix-synapse&amp;quot;;&lt;br /&gt;
    settings = {&lt;br /&gt;
      server_name = config.networking.domain;&lt;br /&gt;
      public_baseurl = &amp;quot;https://matrix.csclub.uwaterloo.ca/&amp;quot;;&lt;br /&gt;
      listeners = [&lt;br /&gt;
        {&lt;br /&gt;
          port = 8008;&lt;br /&gt;
          bind_addresses = [ &amp;quot;::1&amp;quot; ];&lt;br /&gt;
          type = &amp;quot;http&amp;quot;;&lt;br /&gt;
          tls = false;&lt;br /&gt;
          x_forwarded = true;&lt;br /&gt;
          resources = [&lt;br /&gt;
            {&lt;br /&gt;
                names = [ &amp;quot;client&amp;quot; ]; # no federation&lt;br /&gt;
                compress = true;&lt;br /&gt;
            }&lt;br /&gt;
          ];&lt;br /&gt;
        }&lt;br /&gt;
      ];&lt;br /&gt;
&lt;br /&gt;
      oidc_providers = [&lt;br /&gt;
        {&lt;br /&gt;
           idp_id = &amp;quot;keycloak&amp;quot;;&lt;br /&gt;
           idp_name = &amp;quot;CSC&amp;quot;;&lt;br /&gt;
           issuer = &amp;quot;https://keycloak.csclub.uwaterloo.ca/realms/csc&amp;quot;;&lt;br /&gt;
           client_id = &amp;quot;synapse&amp;quot;;&lt;br /&gt;
           client_secret = &amp;quot;xxxx&amp;quot;; # fill the client secret from keycloak here&lt;br /&gt;
           scopes = [ &amp;quot;openid&amp;quot; &amp;quot;profile&amp;quot; ];&lt;br /&gt;
           user_mapping_provider.config = {&lt;br /&gt;
                localpart_template = &amp;quot;{{ user.preferred_username }}&amp;quot;;&lt;br /&gt;
                display_name_template = &amp;quot;{{ user.name }}&amp;quot;;&lt;br /&gt;
           };&lt;br /&gt;
&lt;br /&gt;
        }&lt;br /&gt;
      ];&lt;br /&gt;
      registration_shared_secret = &amp;quot;xxxxx&amp;quot;;&lt;br /&gt;
      enable_registration = true;&lt;br /&gt;
      enable_registration_captcha = false;&lt;br /&gt;
      registration_requires_token = true;&lt;br /&gt;
    };&lt;br /&gt;
  };&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Testing ==&lt;br /&gt;
&lt;br /&gt;
Go to https://app.cinny.in/login/matrix.csclub.uwaterloo.ca, and click &amp;quot;Continue with CSC&amp;quot;.&lt;/div&gt;</summary>
		<author><name>K95ma</name></author>
	</entry>
	<entry>
		<id>https://wiki.csclub.uwaterloo.ca/index.php?title=Proxmox&amp;diff=5426</id>
		<title>Proxmox</title>
		<link rel="alternate" type="text/html" href="https://wiki.csclub.uwaterloo.ca/index.php?title=Proxmox&amp;diff=5426"/>
		<updated>2025-09-16T02:22:47Z</updated>

		<summary type="html">&lt;p&gt;K95ma: +&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The Proxmox Vitural Environment (as of 2025-09-15) lives on the citric-acid machine and can be accessed via https://citric-acid.csclub.uwaterloo.ca:8006.&lt;br /&gt;
&lt;br /&gt;
== Setting up Proxmox ==&lt;br /&gt;
To setup proxmox, from `Server View`, open the `Datacenter` page. Then go to `Permissions -&amp;gt; Realms`.&lt;br /&gt;
&lt;br /&gt;
Then just make sure pam is setup lol&lt;br /&gt;
&lt;br /&gt;
== Networking ==&lt;br /&gt;
There are two ways to do networking: network bridge and NAT. Network bridge will put the container/virtual machine on the CSC network (basically side-by-side to proxmox itself), while NAT will encapsulate the container/VM inside a private subnet that is only visible to proxmox host itself.&lt;br /&gt;
&lt;br /&gt;
For services that only exposes HTTP/HTTPS, NAT is more desirable since multiple services can share a host nginx instance, only requiring the host IP to have 80/443 port opened to the Internet, thus saving some IP address in our pool and save some trips to the IST for firewall exemption. But for services that requires custom ports to be opened (for example, BigBlueButton requires a range of UDP ports to be exposed for relaying video streams), using the network bridge and giving the container/VM its own public IP might be easier.&lt;br /&gt;
&lt;br /&gt;
Currently, &amp;lt;code&amp;gt;vmbr0&amp;lt;/code&amp;gt; is used for bridged network and &amp;lt;code&amp;gt;vmbr1&amp;lt;/code&amp;gt; is used for NAT (see [https://pve.proxmox.com/wiki/Network_Configuration#sysadmin_network_masquerading Proxmox&#039;s wiki on NAT networking] for setup instruction). &amp;lt;code&amp;gt;vmbr0&amp;lt;/code&amp;gt; uses the CSC DHCP server, so you can use DHCP there, but &amp;lt;code&amp;gt;vmbr1&amp;lt;/code&amp;gt; requires manual IP assignment.&lt;br /&gt;
&lt;br /&gt;
Note that only using &amp;lt;code&amp;gt;vmbr1&amp;lt;/code&amp;gt; requires you to use SSH ProxyJump via citric-acid to access the inner container, as it wouldn&#039;t have a public IP.&lt;/div&gt;</summary>
		<author><name>K95ma</name></author>
	</entry>
	<entry>
		<id>https://wiki.csclub.uwaterloo.ca/index.php?title=Keycloak&amp;diff=5425</id>
		<title>Keycloak</title>
		<link rel="alternate" type="text/html" href="https://wiki.csclub.uwaterloo.ca/index.php?title=Keycloak&amp;diff=5425"/>
		<updated>2025-09-16T01:20:31Z</updated>

		<summary type="html">&lt;p&gt;K95ma: fix&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;We are using [https://www.keycloak.org/ Keycloak] for web SSO (Single Sign-On). Clients may use Keycloak for authenticating users via SAML or OIDC (OpenID Connect).&lt;br /&gt;
&lt;br /&gt;
* Admin login: https://keycloak.csclub.uwaterloo.ca/admin&lt;br /&gt;
* Regular user login: https://keycloak.csclub.uwaterloo.ca/realms/csc/account&lt;br /&gt;
* OIDC Auto Discovery URL: https://keycloak.csclub.uwaterloo.ca/realms/csc/.well-known/openid-configuration&lt;br /&gt;
&lt;br /&gt;
== Prerequisites ==&lt;br /&gt;
OK so before we get started, there&#039;s this really useful feature in Keycloak called &amp;quot;Conditional user attribute&amp;quot; which allows you to create a flow which branches based on attributes a user may have. For some reason, this is enabled in the test suite for Keycloak, but is not available from the main application. So we&#039;re going to compile and inject it ourselves.&lt;br /&gt;
&lt;br /&gt;
Clone https://git.csclub.uwaterloo.ca/public/keycloak-spi and run &amp;lt;code&amp;gt;mvn clean package&amp;lt;/code&amp;gt;. This will create a JAR file called csc-keycloak-spi.jar in the target directory; upload this to somewhere where it can be easily downloaded, e.g. your www directory.&lt;br /&gt;
&lt;br /&gt;
== Database setup ==&lt;br /&gt;
Go to biloba or chamomile, run &amp;lt;code&amp;gt;mysql&amp;lt;/code&amp;gt;, and run the following:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
CREATE USER &#039;keycloak&#039; IDENTIFIED BY &#039;replace_this_password&#039;;    &lt;br /&gt;
CREATE DATABASE keycloak CHARACTER SET utf8 COLLATE utf8_unicode_ci;    &lt;br /&gt;
GRANT ALL PRIVILEGES ON keycloak.* TO &#039;keycloak&#039;;    &lt;br /&gt;
FLUSH PRIVILEGES;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Kubernetes setup ==&lt;br /&gt;
We are running Keycloak on Kubernetes. This introduces some complications because it gets reverse proxied twice, and we also can&#039;t (or at least shouldn&#039;t) modify the filesystem of the Pod where it&#039;s running, since that Pod can get destroyed at any time. We still need to load that JAR file we just created, though, so we&#039;re going to place it into a PersistentVolume instead. We&#039;re going to do this by first creating a PersistentVolumeClaim, then claiming it in a temporary Pod which we&#039;ll use for shell access:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
cat &amp;lt;&amp;lt;EOF | kubectl apply -f    &lt;br /&gt;
apiVersion: v1    &lt;br /&gt;
kind: PersistentVolumeClaim    &lt;br /&gt;
metadata:    &lt;br /&gt;
  namespace: syscom    &lt;br /&gt;
  name: keycloak-spi-pvc    &lt;br /&gt;
spec:    &lt;br /&gt;
  storageClassName: cloudstack-storage    &lt;br /&gt;
  accessModes:    &lt;br /&gt;
    - ReadWriteOnce    &lt;br /&gt;
  resources:    &lt;br /&gt;
    requests:    &lt;br /&gt;
      storage: 5Mi    &lt;br /&gt;
---    &lt;br /&gt;
apiVersion: v1    &lt;br /&gt;
kind: Pod    &lt;br /&gt;
metadata:    &lt;br /&gt;
  namespace: syscom    &lt;br /&gt;
  name: temp-pod    &lt;br /&gt;
spec:    &lt;br /&gt;
  containers:    &lt;br /&gt;
    - name: temp    &lt;br /&gt;
      image: alpine    &lt;br /&gt;
      volumeMounts:    &lt;br /&gt;
        - mountPath: &amp;quot;/data&amp;quot;    &lt;br /&gt;
          name: keycloak-spi-pv    &lt;br /&gt;
      stdin: true    &lt;br /&gt;
      stdinOnce: true    &lt;br /&gt;
      tty: true    &lt;br /&gt;
  volumes:    &lt;br /&gt;
    - name: keycloak-spi-pv    &lt;br /&gt;
      persistentVolumeClaim:    &lt;br /&gt;
        claimName: keycloak-spi-pvc&lt;br /&gt;
EOF&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Run &amp;lt;code&amp;gt;kubectl -n syscom get pods&amp;lt;/code&amp;gt; a few times to check if the pod is ready; once it is, attach to it:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
kubectl -n syscom exec -it temp-pod -- sh&lt;br /&gt;
cd /data&lt;br /&gt;
mkdir keycloak-spi&lt;br /&gt;
chmod a+w keycloak-spi&lt;br /&gt;
cd keycloak-spi&lt;br /&gt;
wget https://csclub.uwaterloo.ca/~merenber/csc-keycloak-spi.jar&lt;br /&gt;
exit&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now delete the pod since we don&#039;t need it anymore:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
kubectl -n syscom delete pod temp-pod&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Create some secrets (use the MySQL password which you chose earlier):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
kubectl -n syscom create secret generic keycloak-secret \&lt;br /&gt;
  --from-literal=DB_USER=some_user \&lt;br /&gt;
  --from-literal=DB_PASSWORD=some_password \&lt;br /&gt;
  --from-literal=KEYCLOAK_USER=some_user \&lt;br /&gt;
  --from-literal=KEYCLOAK_PASSWORD=some_password&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now apply the main manifest:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
kubectl apply -f https://git.csclub.uwaterloo.ca/cloud/manifests/raw/branch/master/keycloak.yaml&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== DNS setup ==&lt;br /&gt;
From Infoblox, make keycloak.csclub.uwaterloo.ca a CNAME for rr-public-cloud.csclub.uwaterloo.ca; that record points to biloba and chamomile, which know how to reverse proxy requests to Kubernetes.&lt;br /&gt;
&lt;br /&gt;
== NGINX setup ==&lt;br /&gt;
Pretty standard stuff:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
server {&lt;br /&gt;
  listen 80;&lt;br /&gt;
  listen [::]:80;&lt;br /&gt;
  server_name keycloak.csclub.uwaterloo.ca;&lt;br /&gt;
  return 301 https://$host$request_uri;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
server {&lt;br /&gt;
  listen 443 ssl http2;&lt;br /&gt;
  listen [::]:443 ssl http2;&lt;br /&gt;
  server_name keycloak.csclub.uwaterloo.ca;&lt;br /&gt;
  ssl_certificate /etc/ssl/private/csclub.uwaterloo.ca.chain;&lt;br /&gt;
  ssl_certificate_key /etc/ssl/private/csclub.uwaterloo.ca.key;&lt;br /&gt;
  &lt;br /&gt;
  location / {&lt;br /&gt;
    proxy_pass http://k8s;&lt;br /&gt;
  }&lt;br /&gt;
  include proxy_params;&lt;br /&gt;
&lt;br /&gt;
  access_log /var/log/nginx/keycloak-access.log;&lt;br /&gt;
  error_log /var/log/nginx/keycloak-error.log;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Also make sure you have the following snippet in /etc/nginx/proxy_params:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# Increase buffer size&lt;br /&gt;
# See https://ma.ttias.be/nginx-proxy-upstream-sent-big-header-reading-response-header-upstream/&lt;br /&gt;
proxy_buffer_size 128k;&lt;br /&gt;
proxy_buffers 4 256k;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Don&#039;t forget to enable the site and reload NGINX on both chamomile and biloba.&lt;br /&gt;
&lt;br /&gt;
== Web UI setup ==&lt;br /&gt;
If all went well, you should now be able to visit https://keycloak.csclub.uwaterloo.ca from your browser.&lt;br /&gt;
Create a new realm called &#039;csc&#039;. Set the Display Name to &#039;Computer Science Club&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Realm settings ===&lt;br /&gt;
From the web UI, go to &#039;Realm Settings&#039;, click the Login tab, and disable &#039;Login with email&#039;.&lt;br /&gt;
&lt;br /&gt;
Now click on the &#039;Tokens&#039; tab. Set &#039;SSO Session Idle&#039; and &#039;SSO Session Max&#039; to 120 days each. You can optionally do this for the Master realm as well.&lt;br /&gt;
&lt;br /&gt;
Now click on the &#039;Email&#039; tab.&lt;br /&gt;
&lt;br /&gt;
* Set &#039;Host&#039; to &#039;mail.csclub.uwaterloo.ca&#039;.&lt;br /&gt;
* Set &#039;From Display Name&#039; to &#039;Keycloak&#039;.&lt;br /&gt;
* Set &#039;From&#039; to &#039;no-reply@csclub.uwaterloo.ca&#039;.&lt;br /&gt;
* Set &#039;Reply To Display Name&#039; to &#039;Systems Committee&#039;.&lt;br /&gt;
* Set &#039;Reply To&#039; to &#039;syscom@csclub.uwaterloo.ca&#039;.&lt;br /&gt;
* Set &#039;Envelope From&#039; to &#039;keycloak@csclub.uwaterloo.ca&#039;.&lt;br /&gt;
* Enable StartTLS.&lt;br /&gt;
&lt;br /&gt;
=== Authentication Settings ===&lt;br /&gt;
Click on &#039;Authentication&#039; from the panel on the left-hand side.&lt;br /&gt;
&lt;br /&gt;
==== Flows ====&lt;br /&gt;
Click on the &#039;Flows&#039; tab and select the &#039;Browser&#039; flow from the dropdown. Create a copy called &#039;CSC Browser&#039; and make it look like this:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Cookie (Alternative)&lt;br /&gt;
CSC Browser Forms (Alternative)&lt;br /&gt;
    Username Password Form (Required)&lt;br /&gt;
    CSC Membership Check (Conditional)&lt;br /&gt;
        Condition - User Attribute (Required)&lt;br /&gt;
        Deny Access (Required)&lt;br /&gt;
    OTP Form (Required)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
The condition should be whether a user has the attribute &#039;shadowExpire&#039; set to &#039;1&#039;. Set the error message in &#039;Deny Access&#039; to something reasonable (this gets displayed to users).&lt;br /&gt;
&lt;br /&gt;
Screenshot:&lt;br /&gt;
[[File:Keycloak_CSC_Browser_Flow.png]]&lt;br /&gt;
&lt;br /&gt;
Select the &#039;First Broker Login&#039; flow and create a copy called &#039;CSC First Broker Login&#039;. This will be invoked when a user signs in with an Identity Provider, and the IdP user ID is not linked to a Keycloak user. Make the flow look like this:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Review Profile (Disabled)&lt;br /&gt;
User Creation or Linking (Required)&lt;br /&gt;
    Detect Existing Broker User (Required)&lt;br /&gt;
    Automatically Set Existing User (Required)&lt;br /&gt;
CSC Membership Check (Conditional)&lt;br /&gt;
    Condition - User Attribute (Required)&lt;br /&gt;
    Deny Access (Required)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Finally, create a new flow called &#039;CSC Browser - IdP post-login&#039;. This will be invoked when a user signs in with an IdP, and the IdP user ID has already been linked to a Keycloak user. Make it look like this:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
CSC Membership Check - IdP Post-login (Conditional)&lt;br /&gt;
    Condition - User Attribute (Membership expired - IdP post-login) (Required)&lt;br /&gt;
    Deny Access (Denied because membership expired - IdP post-login) (Required)&lt;br /&gt;
Allow Access&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Note that the last step needs to be &#039;Allow Access&#039;.&lt;br /&gt;
&lt;br /&gt;
==== Bindings ====&lt;br /&gt;
Click on the &#039;Bindings&#039; tab and set &#039;Browser Flow&#039; to &#039;CSC Browser&#039;.&lt;br /&gt;
&lt;br /&gt;
==== Required Actions ====&lt;br /&gt;
Click on the &#039;Required Actions&#039; tab and &amp;lt;b&amp;gt;disable&amp;lt;/b&amp;gt; the following:&lt;br /&gt;
&lt;br /&gt;
* Update Password&lt;br /&gt;
* Update Profile&lt;br /&gt;
* Delete Account&lt;br /&gt;
&lt;br /&gt;
=== User Federation ===&lt;br /&gt;
Click on &#039;User Federation&#039; from the left-hand panel and select &#039;ldap&#039;. Configure it as follows:&lt;br /&gt;
&lt;br /&gt;
* Edit Mode: READ_ONLY&lt;br /&gt;
* Vendor: Other&lt;br /&gt;
* Username LDAP attribute: uid&lt;br /&gt;
* RDN LDAP attribute: uid&lt;br /&gt;
* UUID LDAP attribute: entryUUID&lt;br /&gt;
* User Object Classes: member&lt;br /&gt;
* Connection URL: ldaps://ldap1.csclub.uwaterloo.ca ldaps://ldap2.csclub.uwaterloo.ca&lt;br /&gt;
* Users DN: ou=People,dc=csclub,dc=uwaterloo,dc=ca&lt;br /&gt;
* Custom User LDAP Filter: (objectClass=member)&lt;br /&gt;
* Search Scope: One Level&lt;br /&gt;
* Bind Type: none&lt;br /&gt;
&lt;br /&gt;
Under Sync Settings, enable Changed Users Full Sync. Set the Changed Users Sync Period to something reasonable (should be at least once a day).&lt;br /&gt;
&lt;br /&gt;
Click on Mappers, click &#039;email&#039;, and set the LDAP Attribute to &#039;mailLocalAddress&#039;. Also set the LDAP Attribute for &#039;First Name&#039; to &#039;givenName&#039;.&lt;br /&gt;
&lt;br /&gt;
Create a new mapper of type &#039;user-attribute-ldap-mapper&#039;. Set &#039;User Model Attribute&#039; and &#039;LDAP Attribute&#039; to &#039;shadowExpire&#039;. Enable &#039;Always Read Value From LDAP&#039;.&lt;br /&gt;
&lt;br /&gt;
Now click the &#039;Synchronize all users&#039; button from the main LDAP configuration page. &amp;lt;b&amp;gt;This will take a few minutes so be patient&amp;lt;/b&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Identity Providers ===&lt;br /&gt;
We are going to use [[ADFS]] for identity brokering. The idea is to allow members to login using their CSC credentials &amp;lt;b&amp;gt;or&amp;lt;/b&amp;gt; their UW credentials.&lt;br /&gt;
&lt;br /&gt;
==== SAML Passthrough ====&lt;br /&gt;
I created a monstrosity called [https://git.csclub.uwaterloo.ca/merenber/saml-passthrough saml-passthrough]. The idea is to protect a SAML IdP &amp;lt;i&amp;gt;behind another SAML IdP&amp;lt;/i&amp;gt; (ADFS). So basically SAML-inside-SAML.&lt;br /&gt;
&lt;br /&gt;
The reason why we need this is because we can&#039;t change the AssertionConsumerService endpoint URL which we originally submitted to IST (if we really wanted to, we could submit another form, but that requires assistance from a faculty member, and it&#039;s just a big PiTA). This also allows us to &amp;lt;i&amp;gt;multiplex&amp;lt;/i&amp;gt; ADFS information to multiple Keycloak realms, if necessary.&lt;br /&gt;
&lt;br /&gt;
Anywho, the instructions for setting it up are in the README (it&#039;s just a FastCGI application). It should be running on caffeine right now under /srv/saml-passthrough. It&#039;s configured to download SP metadata from Keycloak at startup, so if you update the SP certificate in Keycloak, make sure to restart the saml-passthrough service.&lt;br /&gt;
&lt;br /&gt;
Now, back to the Keycloak UI. Click &#039;Identity Providers&#039; from the left-hand side, and add a SAML provider. Set the following:&lt;br /&gt;
&lt;br /&gt;
* Alias: adfs&lt;br /&gt;
* Display Name: University of Waterloo (ADFS)&lt;br /&gt;
* First Login Flow: CSC First Login Flow&lt;br /&gt;
* Post Login Flow: CSC Browser - IdP post-login&lt;br /&gt;
* Sync Mode: legacy&lt;br /&gt;
* Service Provider Entity ID: https://keycloak.csclub.uwaterloo.ca/auth/realms/csc&lt;br /&gt;
* Single Sign-On Service URL: https://csclub.uwaterloo.ca/keycloak/saml/sso&lt;br /&gt;
* Principal Type: Attribute [Friendly Name]&lt;br /&gt;
* Principal Attribute: uid&lt;br /&gt;
* HTTP-POST Binding Response: ON&lt;br /&gt;
* Want AuthnRequests Signed: ON&lt;br /&gt;
* Want Assertions Signed: ON&lt;br /&gt;
* Validate Signature: ON&lt;br /&gt;
* Validating X509 certificates: download the content of https://csclub.uwaterloo.ca/keycloak/saml/metadata and copy and paste the value inside the &amp;lt;code&amp;gt;&amp;lt;X509Certificate&amp;gt;&amp;lt;/code&amp;gt; tag.&lt;br /&gt;
&lt;br /&gt;
That X509 certificate will expire in &amp;lt;b&amp;gt;January 2032&amp;lt;/b&amp;gt;; before then, make sure you do the following:&lt;br /&gt;
&lt;br /&gt;
* create a new keypair in /srv/saml-passthrough on caffeine&lt;br /&gt;
* restart the saml-passthrough service&lt;br /&gt;
* update the &#039;Validating X509 certificates&#039; field in Keycloak&lt;br /&gt;
&lt;br /&gt;
Keycloak&#039;s SP certificate (which can be viewed from Realm Settings -&amp;gt; Keys -&amp;gt; Providers -&amp;gt; rsa-generated) will expire in &amp;lt;b&amp;gt;December 2031&amp;lt;/b&amp;gt;; before then, make sure you upload a new keypair (just choose &#039;rsa-generated&#039; from the &#039;Add keystore&#039; dropdown), then restart the saml-passthrough service on caffeine.&lt;br /&gt;
&lt;br /&gt;
== Testing ==&lt;br /&gt;
Here&#039;s a test OIDC application which you can use to verify that everything is working: https://www.keycloak.org/app/#url=https://keycloak.csclub.uwaterloo.ca/&amp;amp;realm=csc&amp;amp;client=test&lt;/div&gt;</summary>
		<author><name>K95ma</name></author>
	</entry>
	<entry>
		<id>https://wiki.csclub.uwaterloo.ca/index.php?title=Keycloak&amp;diff=5424</id>
		<title>Keycloak</title>
		<link rel="alternate" type="text/html" href="https://wiki.csclub.uwaterloo.ca/index.php?title=Keycloak&amp;diff=5424"/>
		<updated>2025-09-16T01:05:24Z</updated>

		<summary type="html">&lt;p&gt;K95ma: /* Testing */ fix&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;We are using [https://www.keycloak.org/ Keycloak] for web SSO (Single Sign-On). Clients may use Keycloak for authenticating users via SAML or OIDC (OpenID Connect).&lt;br /&gt;
&lt;br /&gt;
* Admin login: https://keycloak.csclub.uwaterloo.ca/auth/admin&lt;br /&gt;
* Regular user login: https://keycloak.csclub.uwaterloo.ca/auth/realms/csc/account&lt;br /&gt;
* OIDC Auto Discovery URL: https://keycloak.csclub.uwaterloo.ca/auth/realms/csc/.well-known/openid-configuration&lt;br /&gt;
&lt;br /&gt;
== Prerequisites ==&lt;br /&gt;
OK so before we get started, there&#039;s this really useful feature in Keycloak called &amp;quot;Conditional user attribute&amp;quot; which allows you to create a flow which branches based on attributes a user may have. For some reason, this is enabled in the test suite for Keycloak, but is not available from the main application. So we&#039;re going to compile and inject it ourselves.&lt;br /&gt;
&lt;br /&gt;
Clone https://git.csclub.uwaterloo.ca/public/keycloak-spi and run &amp;lt;code&amp;gt;mvn clean package&amp;lt;/code&amp;gt;. This will create a JAR file called csc-keycloak-spi.jar in the target directory; upload this to somewhere where it can be easily downloaded, e.g. your www directory.&lt;br /&gt;
&lt;br /&gt;
== Database setup ==&lt;br /&gt;
Go to biloba or chamomile, run &amp;lt;code&amp;gt;mysql&amp;lt;/code&amp;gt;, and run the following:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
CREATE USER &#039;keycloak&#039; IDENTIFIED BY &#039;replace_this_password&#039;;    &lt;br /&gt;
CREATE DATABASE keycloak CHARACTER SET utf8 COLLATE utf8_unicode_ci;    &lt;br /&gt;
GRANT ALL PRIVILEGES ON keycloak.* TO &#039;keycloak&#039;;    &lt;br /&gt;
FLUSH PRIVILEGES;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Kubernetes setup ==&lt;br /&gt;
We are running Keycloak on Kubernetes. This introduces some complications because it gets reverse proxied twice, and we also can&#039;t (or at least shouldn&#039;t) modify the filesystem of the Pod where it&#039;s running, since that Pod can get destroyed at any time. We still need to load that JAR file we just created, though, so we&#039;re going to place it into a PersistentVolume instead. We&#039;re going to do this by first creating a PersistentVolumeClaim, then claiming it in a temporary Pod which we&#039;ll use for shell access:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
cat &amp;lt;&amp;lt;EOF | kubectl apply -f    &lt;br /&gt;
apiVersion: v1    &lt;br /&gt;
kind: PersistentVolumeClaim    &lt;br /&gt;
metadata:    &lt;br /&gt;
  namespace: syscom    &lt;br /&gt;
  name: keycloak-spi-pvc    &lt;br /&gt;
spec:    &lt;br /&gt;
  storageClassName: cloudstack-storage    &lt;br /&gt;
  accessModes:    &lt;br /&gt;
    - ReadWriteOnce    &lt;br /&gt;
  resources:    &lt;br /&gt;
    requests:    &lt;br /&gt;
      storage: 5Mi    &lt;br /&gt;
---    &lt;br /&gt;
apiVersion: v1    &lt;br /&gt;
kind: Pod    &lt;br /&gt;
metadata:    &lt;br /&gt;
  namespace: syscom    &lt;br /&gt;
  name: temp-pod    &lt;br /&gt;
spec:    &lt;br /&gt;
  containers:    &lt;br /&gt;
    - name: temp    &lt;br /&gt;
      image: alpine    &lt;br /&gt;
      volumeMounts:    &lt;br /&gt;
        - mountPath: &amp;quot;/data&amp;quot;    &lt;br /&gt;
          name: keycloak-spi-pv    &lt;br /&gt;
      stdin: true    &lt;br /&gt;
      stdinOnce: true    &lt;br /&gt;
      tty: true    &lt;br /&gt;
  volumes:    &lt;br /&gt;
    - name: keycloak-spi-pv    &lt;br /&gt;
      persistentVolumeClaim:    &lt;br /&gt;
        claimName: keycloak-spi-pvc&lt;br /&gt;
EOF&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Run &amp;lt;code&amp;gt;kubectl -n syscom get pods&amp;lt;/code&amp;gt; a few times to check if the pod is ready; once it is, attach to it:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
kubectl -n syscom exec -it temp-pod -- sh&lt;br /&gt;
cd /data&lt;br /&gt;
mkdir keycloak-spi&lt;br /&gt;
chmod a+w keycloak-spi&lt;br /&gt;
cd keycloak-spi&lt;br /&gt;
wget https://csclub.uwaterloo.ca/~merenber/csc-keycloak-spi.jar&lt;br /&gt;
exit&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now delete the pod since we don&#039;t need it anymore:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
kubectl -n syscom delete pod temp-pod&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Create some secrets (use the MySQL password which you chose earlier):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
kubectl -n syscom create secret generic keycloak-secret \&lt;br /&gt;
  --from-literal=DB_USER=some_user \&lt;br /&gt;
  --from-literal=DB_PASSWORD=some_password \&lt;br /&gt;
  --from-literal=KEYCLOAK_USER=some_user \&lt;br /&gt;
  --from-literal=KEYCLOAK_PASSWORD=some_password&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now apply the main manifest:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
kubectl apply -f https://git.csclub.uwaterloo.ca/cloud/manifests/raw/branch/master/keycloak.yaml&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== DNS setup ==&lt;br /&gt;
From Infoblox, make keycloak.csclub.uwaterloo.ca a CNAME for rr-public-cloud.csclub.uwaterloo.ca; that record points to biloba and chamomile, which know how to reverse proxy requests to Kubernetes.&lt;br /&gt;
&lt;br /&gt;
== NGINX setup ==&lt;br /&gt;
Pretty standard stuff:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
server {&lt;br /&gt;
  listen 80;&lt;br /&gt;
  listen [::]:80;&lt;br /&gt;
  server_name keycloak.csclub.uwaterloo.ca;&lt;br /&gt;
  return 301 https://$host$request_uri;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
server {&lt;br /&gt;
  listen 443 ssl http2;&lt;br /&gt;
  listen [::]:443 ssl http2;&lt;br /&gt;
  server_name keycloak.csclub.uwaterloo.ca;&lt;br /&gt;
  ssl_certificate /etc/ssl/private/csclub.uwaterloo.ca.chain;&lt;br /&gt;
  ssl_certificate_key /etc/ssl/private/csclub.uwaterloo.ca.key;&lt;br /&gt;
  &lt;br /&gt;
  location / {&lt;br /&gt;
    proxy_pass http://k8s;&lt;br /&gt;
  }&lt;br /&gt;
  include proxy_params;&lt;br /&gt;
&lt;br /&gt;
  access_log /var/log/nginx/keycloak-access.log;&lt;br /&gt;
  error_log /var/log/nginx/keycloak-error.log;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Also make sure you have the following snippet in /etc/nginx/proxy_params:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# Increase buffer size&lt;br /&gt;
# See https://ma.ttias.be/nginx-proxy-upstream-sent-big-header-reading-response-header-upstream/&lt;br /&gt;
proxy_buffer_size 128k;&lt;br /&gt;
proxy_buffers 4 256k;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Don&#039;t forget to enable the site and reload NGINX on both chamomile and biloba.&lt;br /&gt;
&lt;br /&gt;
== Web UI setup ==&lt;br /&gt;
If all went well, you should now be able to visit https://keycloak.csclub.uwaterloo.ca from your browser.&lt;br /&gt;
Create a new realm called &#039;csc&#039;. Set the Display Name to &#039;Computer Science Club&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Realm settings ===&lt;br /&gt;
From the web UI, go to &#039;Realm Settings&#039;, click the Login tab, and disable &#039;Login with email&#039;.&lt;br /&gt;
&lt;br /&gt;
Now click on the &#039;Tokens&#039; tab. Set &#039;SSO Session Idle&#039; and &#039;SSO Session Max&#039; to 120 days each. You can optionally do this for the Master realm as well.&lt;br /&gt;
&lt;br /&gt;
Now click on the &#039;Email&#039; tab.&lt;br /&gt;
&lt;br /&gt;
* Set &#039;Host&#039; to &#039;mail.csclub.uwaterloo.ca&#039;.&lt;br /&gt;
* Set &#039;From Display Name&#039; to &#039;Keycloak&#039;.&lt;br /&gt;
* Set &#039;From&#039; to &#039;no-reply@csclub.uwaterloo.ca&#039;.&lt;br /&gt;
* Set &#039;Reply To Display Name&#039; to &#039;Systems Committee&#039;.&lt;br /&gt;
* Set &#039;Reply To&#039; to &#039;syscom@csclub.uwaterloo.ca&#039;.&lt;br /&gt;
* Set &#039;Envelope From&#039; to &#039;keycloak@csclub.uwaterloo.ca&#039;.&lt;br /&gt;
* Enable StartTLS.&lt;br /&gt;
&lt;br /&gt;
=== Authentication Settings ===&lt;br /&gt;
Click on &#039;Authentication&#039; from the panel on the left-hand side.&lt;br /&gt;
&lt;br /&gt;
==== Flows ====&lt;br /&gt;
Click on the &#039;Flows&#039; tab and select the &#039;Browser&#039; flow from the dropdown. Create a copy called &#039;CSC Browser&#039; and make it look like this:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Cookie (Alternative)&lt;br /&gt;
CSC Browser Forms (Alternative)&lt;br /&gt;
    Username Password Form (Required)&lt;br /&gt;
    CSC Membership Check (Conditional)&lt;br /&gt;
        Condition - User Attribute (Required)&lt;br /&gt;
        Deny Access (Required)&lt;br /&gt;
    OTP Form (Required)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
The condition should be whether a user has the attribute &#039;shadowExpire&#039; set to &#039;1&#039;. Set the error message in &#039;Deny Access&#039; to something reasonable (this gets displayed to users).&lt;br /&gt;
&lt;br /&gt;
Screenshot:&lt;br /&gt;
[[File:Keycloak_CSC_Browser_Flow.png]]&lt;br /&gt;
&lt;br /&gt;
Select the &#039;First Broker Login&#039; flow and create a copy called &#039;CSC First Broker Login&#039;. This will be invoked when a user signs in with an Identity Provider, and the IdP user ID is not linked to a Keycloak user. Make the flow look like this:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Review Profile (Disabled)&lt;br /&gt;
User Creation or Linking (Required)&lt;br /&gt;
    Detect Existing Broker User (Required)&lt;br /&gt;
    Automatically Set Existing User (Required)&lt;br /&gt;
CSC Membership Check (Conditional)&lt;br /&gt;
    Condition - User Attribute (Required)&lt;br /&gt;
    Deny Access (Required)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Finally, create a new flow called &#039;CSC Browser - IdP post-login&#039;. This will be invoked when a user signs in with an IdP, and the IdP user ID has already been linked to a Keycloak user. Make it look like this:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
CSC Membership Check - IdP Post-login (Conditional)&lt;br /&gt;
    Condition - User Attribute (Membership expired - IdP post-login) (Required)&lt;br /&gt;
    Deny Access (Denied because membership expired - IdP post-login) (Required)&lt;br /&gt;
Allow Access&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Note that the last step needs to be &#039;Allow Access&#039;.&lt;br /&gt;
&lt;br /&gt;
==== Bindings ====&lt;br /&gt;
Click on the &#039;Bindings&#039; tab and set &#039;Browser Flow&#039; to &#039;CSC Browser&#039;.&lt;br /&gt;
&lt;br /&gt;
==== Required Actions ====&lt;br /&gt;
Click on the &#039;Required Actions&#039; tab and &amp;lt;b&amp;gt;disable&amp;lt;/b&amp;gt; the following:&lt;br /&gt;
&lt;br /&gt;
* Update Password&lt;br /&gt;
* Update Profile&lt;br /&gt;
* Delete Account&lt;br /&gt;
&lt;br /&gt;
=== User Federation ===&lt;br /&gt;
Click on &#039;User Federation&#039; from the left-hand panel and select &#039;ldap&#039;. Configure it as follows:&lt;br /&gt;
&lt;br /&gt;
* Edit Mode: READ_ONLY&lt;br /&gt;
* Vendor: Other&lt;br /&gt;
* Username LDAP attribute: uid&lt;br /&gt;
* RDN LDAP attribute: uid&lt;br /&gt;
* UUID LDAP attribute: entryUUID&lt;br /&gt;
* User Object Classes: member&lt;br /&gt;
* Connection URL: ldaps://ldap1.csclub.uwaterloo.ca ldaps://ldap2.csclub.uwaterloo.ca&lt;br /&gt;
* Users DN: ou=People,dc=csclub,dc=uwaterloo,dc=ca&lt;br /&gt;
* Custom User LDAP Filter: (objectClass=member)&lt;br /&gt;
* Search Scope: One Level&lt;br /&gt;
* Bind Type: none&lt;br /&gt;
&lt;br /&gt;
Under Sync Settings, enable Changed Users Full Sync. Set the Changed Users Sync Period to something reasonable (should be at least once a day).&lt;br /&gt;
&lt;br /&gt;
Click on Mappers, click &#039;email&#039;, and set the LDAP Attribute to &#039;mailLocalAddress&#039;. Also set the LDAP Attribute for &#039;First Name&#039; to &#039;givenName&#039;.&lt;br /&gt;
&lt;br /&gt;
Create a new mapper of type &#039;user-attribute-ldap-mapper&#039;. Set &#039;User Model Attribute&#039; and &#039;LDAP Attribute&#039; to &#039;shadowExpire&#039;. Enable &#039;Always Read Value From LDAP&#039;.&lt;br /&gt;
&lt;br /&gt;
Now click the &#039;Synchronize all users&#039; button from the main LDAP configuration page. &amp;lt;b&amp;gt;This will take a few minutes so be patient&amp;lt;/b&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Identity Providers ===&lt;br /&gt;
We are going to use [[ADFS]] for identity brokering. The idea is to allow members to login using their CSC credentials &amp;lt;b&amp;gt;or&amp;lt;/b&amp;gt; their UW credentials.&lt;br /&gt;
&lt;br /&gt;
==== SAML Passthrough ====&lt;br /&gt;
I created a monstrosity called [https://git.csclub.uwaterloo.ca/merenber/saml-passthrough saml-passthrough]. The idea is to protect a SAML IdP &amp;lt;i&amp;gt;behind another SAML IdP&amp;lt;/i&amp;gt; (ADFS). So basically SAML-inside-SAML.&lt;br /&gt;
&lt;br /&gt;
The reason why we need this is because we can&#039;t change the AssertionConsumerService endpoint URL which we originally submitted to IST (if we really wanted to, we could submit another form, but that requires assistance from a faculty member, and it&#039;s just a big PiTA). This also allows us to &amp;lt;i&amp;gt;multiplex&amp;lt;/i&amp;gt; ADFS information to multiple Keycloak realms, if necessary.&lt;br /&gt;
&lt;br /&gt;
Anywho, the instructions for setting it up are in the README (it&#039;s just a FastCGI application). It should be running on caffeine right now under /srv/saml-passthrough. It&#039;s configured to download SP metadata from Keycloak at startup, so if you update the SP certificate in Keycloak, make sure to restart the saml-passthrough service.&lt;br /&gt;
&lt;br /&gt;
Now, back to the Keycloak UI. Click &#039;Identity Providers&#039; from the left-hand side, and add a SAML provider. Set the following:&lt;br /&gt;
&lt;br /&gt;
* Alias: adfs&lt;br /&gt;
* Display Name: University of Waterloo (ADFS)&lt;br /&gt;
* First Login Flow: CSC First Login Flow&lt;br /&gt;
* Post Login Flow: CSC Browser - IdP post-login&lt;br /&gt;
* Sync Mode: legacy&lt;br /&gt;
* Service Provider Entity ID: https://keycloak.csclub.uwaterloo.ca/auth/realms/csc&lt;br /&gt;
* Single Sign-On Service URL: https://csclub.uwaterloo.ca/keycloak/saml/sso&lt;br /&gt;
* Principal Type: Attribute [Friendly Name]&lt;br /&gt;
* Principal Attribute: uid&lt;br /&gt;
* HTTP-POST Binding Response: ON&lt;br /&gt;
* Want AuthnRequests Signed: ON&lt;br /&gt;
* Want Assertions Signed: ON&lt;br /&gt;
* Validate Signature: ON&lt;br /&gt;
* Validating X509 certificates: download the content of https://csclub.uwaterloo.ca/keycloak/saml/metadata and copy and paste the value inside the &amp;lt;code&amp;gt;&amp;lt;X509Certificate&amp;gt;&amp;lt;/code&amp;gt; tag.&lt;br /&gt;
&lt;br /&gt;
That X509 certificate will expire in &amp;lt;b&amp;gt;January 2032&amp;lt;/b&amp;gt;; before then, make sure you do the following:&lt;br /&gt;
&lt;br /&gt;
* create a new keypair in /srv/saml-passthrough on caffeine&lt;br /&gt;
* restart the saml-passthrough service&lt;br /&gt;
* update the &#039;Validating X509 certificates&#039; field in Keycloak&lt;br /&gt;
&lt;br /&gt;
Keycloak&#039;s SP certificate (which can be viewed from Realm Settings -&amp;gt; Keys -&amp;gt; Providers -&amp;gt; rsa-generated) will expire in &amp;lt;b&amp;gt;December 2031&amp;lt;/b&amp;gt;; before then, make sure you upload a new keypair (just choose &#039;rsa-generated&#039; from the &#039;Add keystore&#039; dropdown), then restart the saml-passthrough service on caffeine.&lt;br /&gt;
&lt;br /&gt;
== Testing ==&lt;br /&gt;
Here&#039;s a test OIDC application which you can use to verify that everything is working: https://www.keycloak.org/app/#url=https://keycloak.csclub.uwaterloo.ca/&amp;amp;realm=csc&amp;amp;client=test&lt;/div&gt;</summary>
		<author><name>K95ma</name></author>
	</entry>
	<entry>
		<id>https://wiki.csclub.uwaterloo.ca/index.php?title=User:K95ma&amp;diff=5421</id>
		<title>User:K95ma</title>
		<link rel="alternate" type="text/html" href="https://wiki.csclub.uwaterloo.ca/index.php?title=User:K95ma&amp;diff=5421"/>
		<updated>2025-09-13T01:54:11Z</updated>

		<summary type="html">&lt;p&gt;K95ma: Created page with &amp;quot;Termcom guy&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Termcom guy&lt;/div&gt;</summary>
		<author><name>K95ma</name></author>
	</entry>
	<entry>
		<id>https://wiki.csclub.uwaterloo.ca/index.php?title=LDAP&amp;diff=5420</id>
		<title>LDAP</title>
		<link rel="alternate" type="text/html" href="https://wiki.csclub.uwaterloo.ca/index.php?title=LDAP&amp;diff=5420"/>
		<updated>2025-09-10T00:58:05Z</updated>

		<summary type="html">&lt;p&gt;K95ma: /* Querying LDAP */ fix command&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;We use [http://www.openldap.org/ OpenLDAP] for directory services. Our primary LDAP server is [[Machine_List#auth1|auth1]] and our secondary LDAP server is [[Machine_List#auth2|auth2]].&lt;br /&gt;
&lt;br /&gt;
=== ehashman&#039;s Guide to Setting up OpenLDAP on Debian ===&lt;br /&gt;
&lt;br /&gt;
Welcome to my nightmare.&lt;br /&gt;
&lt;br /&gt;
==== What is LDAP? ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&#039;&#039;&#039;LDAP:&#039;&#039;&#039; Lightweight Directory Access Protocol&lt;br /&gt;
&lt;br /&gt;
An open, vendor-neutral, industry standard application protocol for accessing and maintaining distributed directory information services over an Internet Protocol (IP) network. — [https://en.wikipedia.org/wiki/Lightweight_Directory_Access_Protocol Wikipedia: LDAP]&lt;br /&gt;
&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
In this case, &amp;amp;quot;directory&amp;amp;quot; refers to the user directory, like on an old-school Rolodex. Many groups use LDAP to maintain their user directory, including the University (the &amp;amp;quot;WatIAM&amp;amp;quot; identity management system), the Computer Science Club, and even the UW Amateur Radio Club.&lt;br /&gt;
&lt;br /&gt;
This is a guide documenting how to set up LDAP on a Debian Linux system.&lt;br /&gt;
&lt;br /&gt;
==== First steps ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt;Ensure that openldap is installed on the machine:&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# apt-get install slapd ldap-utils&amp;lt;/pre&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Debian will do a lot of magic and set up a skeleton LDAP server and get it running. We need to configure that further.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt;Let&#039;s set up logging before we forget. Create the following files in &amp;lt;code&amp;gt;/var/log&amp;lt;/code&amp;gt;:&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# mkdir /var/log/ldap&lt;br /&gt;
# touch /var/log/ldap.log&amp;lt;/pre&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt;Set ownership correctly:&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# chown openldap:openldap /var/log/ldap&amp;lt;/pre&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt;Set up rsyslog to dump the LDAP logs into &amp;lt;code&amp;gt;/var/log/ldap.log&amp;lt;/code&amp;gt; by adding the following lines:&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# vim /etc/rsyslog.conf&lt;br /&gt;
...&lt;br /&gt;
# Grab ldap logs, don&#039;t duplicate in syslog&lt;br /&gt;
local4.*                        /var/log/ldap.log&amp;lt;/pre&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt;Set up log rotation for these by creating the file [https://git.uwaterloo.ca/wics/documentation/blob/master/ldap/logrotate.d.ldap &amp;lt;code&amp;gt;/etc/logrotate.d/ldap&amp;lt;/code&amp;gt;] with the following contents:&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;/var/log/ldap/*log {&lt;br /&gt;
    weekly&lt;br /&gt;
    missingok&lt;br /&gt;
    rotate 1000&lt;br /&gt;
    compress&lt;br /&gt;
    delaycompress&lt;br /&gt;
    notifempty&lt;br /&gt;
    create 0640 openldap adm&lt;br /&gt;
    postrotate&lt;br /&gt;
        if [ -f /var/run/slapd/slapd.pid ]; then&lt;br /&gt;
            /etc/init.d/slapd restart &amp;amp;gt;/dev/null 2&amp;amp;gt;&amp;amp;amp;1&lt;br /&gt;
        fi&lt;br /&gt;
    endscript&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/var/log/ldap.log {&lt;br /&gt;
    weekly&lt;br /&gt;
    missingok&lt;br /&gt;
    rotate 24&lt;br /&gt;
    compress&lt;br /&gt;
    delaycompress&lt;br /&gt;
    notifempty&lt;br /&gt;
}&amp;lt;/pre&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt;As of OpenLDAP 2.4, it doesn&#039;t actually create a config file for us. Apparently, this is a &amp;amp;quot;feature&amp;amp;quot;: LDAP maintainers think we should want to set this up via dynamic queries. We don&#039;t, so the first thing we need is our [https://git.uwaterloo.ca/wics/documentation/blob/master/ldap/slapd.conf &amp;lt;code&amp;gt;slapd.conf&amp;lt;/code&amp;gt;] file.&amp;lt;/p&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Building &amp;lt;code&amp;gt;slapd.conf&amp;lt;/code&amp;gt; from scratch =====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt;Get a copy to work with:&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# scp uid@auth1.csclub.uwaterloo.ca:/etc/ldap/slapd.conf /etc/ldap/  ## you need CSC root for this&amp;lt;/pre&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;You&#039;ll want to comment out the TLS lines, and anything referring to Kerberos and access for now. You&#039;ll also want to comment out lines specifically referring to syscom and office staff.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Make sure you remove the reference to &amp;lt;code&amp;gt;nonMemberTerm&amp;lt;/code&amp;gt; as an index, as we&#039;re going to remove this field.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt;You&#039;ll also need to generate a root password for the LDAP to bootstrap auth, like so:&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# slappasswd&lt;br /&gt;
New password: &lt;br /&gt;
Re-enter new password:&lt;br /&gt;
{SSHA}longhash&amp;lt;/pre&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt;Add this line below &amp;lt;code&amp;gt;rootdn&amp;lt;/code&amp;gt; in the &amp;lt;code&amp;gt;slapd.conf&amp;lt;/code&amp;gt;:&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;rootpw          {SSHA}longhash&amp;lt;/pre&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt;Now we want to edit all instances of &amp;amp;quot;csclub&amp;amp;quot; to be &amp;amp;quot;wics&amp;amp;quot; instead, e.g.:&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;suffix     &amp;amp;quot;dc=wics,dc=uwaterloo,dc=ca&amp;amp;quot;&lt;br /&gt;
rootdn     &amp;amp;quot;cn=root,dc=wics,dc=uwaterloo,dc=ca&amp;amp;quot;&amp;lt;/pre&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt;Next, we need to grab all the relevant schemas:&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;scp -r uid@auth1.csclub.uwaterloo.ca:/etc/ldap/schema/ /tmp/schemas&amp;lt;/pre&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Use the include directives to help you find the ones you need. I noticed we were missing &amp;lt;code&amp;gt;sudo.schema&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;csc.schema&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;rfc2307bis.schema&amp;lt;/code&amp;gt;.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Open up the [https://git.uwaterloo.ca/wics/documentation/blob/master/ldap/csc.schema &amp;lt;code&amp;gt;csc.schema&amp;lt;/code&amp;gt;] for editing; we&#039;re not using it verbatim. Remove the attributes &amp;lt;code&amp;gt;studentid&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;nonMemberTerm&amp;lt;/code&amp;gt; and the objectclass &amp;lt;code&amp;gt;club&amp;lt;/code&amp;gt;. Also make sure you change the OID so we don&#039;t clash with the CSC. Because we didn&#039;t want to go through the process of requesting a [http://pen.iana.org/pen/PenApplication.page PEN number], we chose arbitrarily to use 26338, which belongs to IWICS Inc.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt;We also need to can the auto-generated config files, so do that:&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# rm -rf /etc/openldap/slapd.d/*&amp;lt;/pre&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt;Also nuke the auto-generated database:&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# rm /var/lib/ldap/__db.*&amp;lt;/pre&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt;Configure the database:&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# cp /usr/share/slapd/DB_CONFIG /var/lib/ldap/&lt;br /&gt;
# chown openldap:openldap /var/lib/ldap/DB_CONFIG &amp;lt;/pre&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt;Now we can generate the new configuration files:&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# slaptest -f /etc/ldap/slapd.conf -F /etc/ldap/slapd.d/&amp;lt;/pre&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt;And ensure that the permissions are all set correctly, lest this break something:&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# chown -R openldap:openldap /etc/ldap/slapd.d&amp;lt;/pre&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt;If at this point you get a nasty error, such as&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;5657d4db hdb_db_open: database &amp;amp;quot;dc=wics,dc=uwaterloo,dc=ca&amp;amp;quot;: db_open(/var/lib/ldap/id2entry.bdb) failed: No such file or directory (2).&lt;br /&gt;
5657d4db backend_startup_one (type=hdb, suffix=&amp;amp;quot;dc=wics,dc=uwaterloo,dc=ca&amp;amp;quot;): bi_db_open failed! (2)&lt;br /&gt;
slap_startup failed (test would succeed using the -u switch)&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Just try restarting slapd, and see if that fixes the problem:&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# service slapd stop&lt;br /&gt;
# service slapd start&amp;lt;/pre&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt;Congratulations! Your LDAP service is now configured and running.&amp;lt;/p&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Getting TLS Up and Running ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt;Now that we have our LDAP service, we&#039;ll want to be able to serve encrypted traffic. This is especially important for any remote access, since binding to LDAP (i.e. sending it a password for auth) occurs over plaintext, and we don&#039;t want to leak our admin password.&amp;lt;/p&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt;Our first step is to copy our SSL certificates into the correct places. Public ones go into &amp;lt;code&amp;gt;/etc/ssl/certs/&amp;lt;/code&amp;gt; and private ones go into &amp;lt;code&amp;gt;/etc/ssl/private/&amp;lt;/code&amp;gt;.&amp;lt;/p&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt;Since the LDAP daemon needs to be able to read our private cert, we need to grant LDAP access to the private folder:&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# chgrp openldap /etc/ssl/private &lt;br /&gt;
# chmod g+x /etc/ssl/private&amp;lt;/pre&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt;Next, uncomment the TLS-related settings in &amp;lt;code&amp;gt;slapd.conf&amp;lt;/code&amp;gt;. These are &amp;lt;code&amp;gt;TLSCertificateFile&amp;lt;/code&amp;gt; (the public cert), &amp;lt;code&amp;gt;TLSCertificateKeyFile&amp;lt;/code&amp;gt; (the private key), &amp;lt;code&amp;gt;TLSCACertificateFile&amp;lt;/code&amp;gt; (the intermediate CA cert), and &amp;lt;code&amp;gt;TLSVerifyClient&amp;lt;/code&amp;gt; (set to &amp;amp;quot;allow&amp;amp;quot;).&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# enable TLS connections&lt;br /&gt;
TLSCertificateFile      /etc/ssl/certs/wics-wildcard.crt&lt;br /&gt;
TLSCertificateKeyFile   /etc/ssl/private/wics-wildcard.key&lt;br /&gt;
&lt;br /&gt;
# enable TLS client authentication&lt;br /&gt;
TLSCACertificateFile    /etc/ssl/certs/GlobalSign_Intermediate_Root_SHA256_G2.pem&lt;br /&gt;
TLSVerifyClient         allow&amp;lt;/pre&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt;Update all your LDAP settings:&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# rm -rf /etc/openldap/slapd.d/*&lt;br /&gt;
# slaptest -f /etc/ldap/slapd.conf -F /etc/ldap/slapd.d/&lt;br /&gt;
# chown -R openldap:openldap /etc/ldap/slapd.d&amp;lt;/pre&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt;And last, ensure that LDAP will actually serve &amp;lt;code&amp;gt;ldaps://&amp;lt;/code&amp;gt; by modifying the init script variables in &amp;lt;code&amp;gt;/etc/default/&amp;lt;/code&amp;gt;:&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# vim /etc/default/slapd&lt;br /&gt;
...&lt;br /&gt;
SLAPD_SERVICES=&amp;amp;quot;ldap:/// ldapi:/// ldaps:///&amp;amp;quot;&lt;br /&gt;
...&amp;lt;/pre&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt;Now you can restart the LDAP server:&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# service slapd restart&amp;lt;/pre&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt;And assuming this is successful, test to ensure LDAP is serving on port 636 for &amp;lt;code&amp;gt;ldaps://&amp;lt;/code&amp;gt;:&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# netstat -ntaup&lt;br /&gt;
Active Internet connections (servers and established)&lt;br /&gt;
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name&lt;br /&gt;
tcp        0      0 0.0.0.0:389             0.0.0.0:*               LISTEN      22847/slapd     &lt;br /&gt;
tcp        0      0 0.0.0.0:636             0.0.0.0:*               LISTEN      22847/slapd &amp;lt;/pre&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Populating the Database ====&lt;br /&gt;
&lt;br /&gt;
Now you&#039;ll need to start adding objects to the database. While we&#039;ll want to mostly do this programmatically, there are a few entries we&#039;ll need to bootstrap.&lt;br /&gt;
&lt;br /&gt;
===== Root Entries =====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt;Start by creating a file [https://git.uwaterloo.ca/wics/documentation/blob/master/ldap/tree.ldif &amp;lt;code&amp;gt;tree.ldif&amp;lt;/code&amp;gt;] to create a few necessary &amp;amp;quot;roots&amp;amp;quot; in our LDAP tree, with the contents:&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;dn: dc=wics,dc=uwaterloo,dc=ca&lt;br /&gt;
objectClass: dcObject&lt;br /&gt;
objectClass: organization&lt;br /&gt;
o: Women in Computer Science&lt;br /&gt;
dc: wics&lt;br /&gt;
&lt;br /&gt;
dn: ou=People,dc=wics,dc=uwaterloo,dc=ca&lt;br /&gt;
objectClass: organizationalUnit&lt;br /&gt;
ou: People&lt;br /&gt;
&lt;br /&gt;
dn: ou=Group,dc=wics,dc=uwaterloo,dc=ca&lt;br /&gt;
objectClass: organizationalUnit&lt;br /&gt;
ou: Group&amp;lt;/pre&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt;Now attempt an LDAP add, using the password you set earlier:&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# ldapadd -cxWD cn=root,dc=wics,dc=uwaterloo,dc=ca -f tree.ldif&lt;br /&gt;
Enter LDAP Password:&lt;br /&gt;
adding new entry &amp;amp;quot;dc=wics,dc=uwaterloo,dc=ca&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
adding new entry &amp;amp;quot;ou=People,dc=wics,dc=uwaterloo,dc=ca&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
adding new entry &amp;amp;quot;ou=Group,dc=wics,dc=uwaterloo,dc=ca&amp;amp;quot;&amp;lt;/pre&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt;Test that everything turned out okay, by performing a query of the entire database:&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# ldapsearch -x -h localhost&lt;br /&gt;
# extended LDIF&lt;br /&gt;
#&lt;br /&gt;
# LDAPv3&lt;br /&gt;
# base &amp;amp;lt;dc=wics,dc=uwaterloo,dc=ca&amp;amp;gt; (default) with scope subtree&lt;br /&gt;
# filter: (objectclass=*)&lt;br /&gt;
# requesting: ALL&lt;br /&gt;
#&lt;br /&gt;
&lt;br /&gt;
# wics.uwaterloo.ca&lt;br /&gt;
dn: dc=wics,dc=uwaterloo,dc=ca&lt;br /&gt;
objectClass: dcObject&lt;br /&gt;
objectClass: organization&lt;br /&gt;
o: Women in Computer Science&lt;br /&gt;
dc: wics&lt;br /&gt;
&lt;br /&gt;
# People, wics.uwaterloo.ca&lt;br /&gt;
dn: ou=People,dc=wics,dc=uwaterloo,dc=ca&lt;br /&gt;
objectClass: organizationalUnit&lt;br /&gt;
ou: People&lt;br /&gt;
&lt;br /&gt;
# Group, wics.uwaterloo.ca&lt;br /&gt;
dn: ou=Group,dc=wics,dc=uwaterloo,dc=ca&lt;br /&gt;
objectClass: organizationalUnit&lt;br /&gt;
ou: Group&lt;br /&gt;
&lt;br /&gt;
# search result&lt;br /&gt;
search: 2&lt;br /&gt;
result: 0 Success&lt;br /&gt;
&lt;br /&gt;
# numResponses: 4&lt;br /&gt;
# numEntries: 3&amp;lt;/pre&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Users and Groups =====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt;Next, add users to track the current GID and UID. This will save us from querying the entire database every time we make a new user or group. Create this file, [https://git.uwaterloo.ca/wics/documentation/blob/master/ldap/nextxid.ldif &amp;lt;code&amp;gt;nextxid.ldif&amp;lt;/code&amp;gt;]:&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;dn: uid=nextuid,ou=People,dc=wics,dc=uwaterloo,dc=ca&lt;br /&gt;
cn: nextuid&lt;br /&gt;
objectClass: account&lt;br /&gt;
objectClass: posixAccount&lt;br /&gt;
objectClass: top&lt;br /&gt;
uidNumber: 20000&lt;br /&gt;
gidNumber: 20000&lt;br /&gt;
homeDirectory: /dev/null&lt;br /&gt;
&lt;br /&gt;
dn: cn=nextgid,ou=Group,dc=wics,dc=uwaterloo,dc=ca&lt;br /&gt;
objectClass: group&lt;br /&gt;
objectClass: posixGroup&lt;br /&gt;
objectClass: top&lt;br /&gt;
gidNumber: 10000&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;You&#039;ll see here that our first GID is 10000 and our first UID is 20000.&amp;lt;/p&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt;Now add them, like you did with the roots of the tree:&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# ldapadd -cxWD cn=root,dc=wics,dc=uwaterloo,dc=ca -f nextxid.ldif&lt;br /&gt;
Enter LDAP Password:&lt;br /&gt;
adding new entry &amp;amp;quot;uid=nextuid,ou=People,dc=wics,dc=uwaterloo,dc=ca&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
adding new entry &amp;amp;quot;cn=nextgid,ou=Group,dc=wics,dc=uwaterloo,dc=ca&amp;amp;quot;&amp;lt;/pre&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Special &amp;lt;code&amp;gt;sudo&amp;lt;/code&amp;gt; Entries =====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt;We also need to add a sudoers OU with a defaults object for default sudo settings. We also need entries for syscom, such that members of the syscom group can use sudo on all hosts, and for termcom, whose members can use sudo on only the office terminals. Call this one [https://git.uwaterloo.ca/wics/documentation/blob/master/ldap/sudoers.ldif &amp;lt;code&amp;gt;sudoers.ldif&amp;lt;/code&amp;gt;]:&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;dn: ou=SUDOers,dc=wics,dc=uwaterloo,dc=ca&lt;br /&gt;
objectClass: organizationalUnit&lt;br /&gt;
ou: SUDOers&lt;br /&gt;
&lt;br /&gt;
dn: cn=defaults,ou=SUDOers,dc=wics,dc=uwaterloo,dc=ca&lt;br /&gt;
objectClass: top&lt;br /&gt;
objectClass: sudoRole&lt;br /&gt;
cn: defaults&lt;br /&gt;
sudoOption: !lecture&lt;br /&gt;
sudoOption: env_reset&lt;br /&gt;
sudoOption: listpw=never&lt;br /&gt;
sudoOption: mailto=&amp;amp;quot;wics-sys@lists.uwaterloo.ca&amp;amp;quot;&lt;br /&gt;
sudoOption: shell_noargs&lt;br /&gt;
&lt;br /&gt;
dn: cn=%syscom,ou=SUDOers,dc=wics,dc=uwaterloo,dc=ca&lt;br /&gt;
objectClass: top&lt;br /&gt;
objectClass: sudoRole&lt;br /&gt;
cn: %syscom&lt;br /&gt;
sudoUser: %syscom&lt;br /&gt;
sudoHost: ALL&lt;br /&gt;
sudoCommand: ALL&lt;br /&gt;
sudoRunAsUser: ALL&lt;br /&gt;
&lt;br /&gt;
dn: cn=%termcom,ou=SUDOers,dc=wics,dc=uwaterloo,dc=ca&lt;br /&gt;
objectClass: top&lt;br /&gt;
objectClass: sudoRole&lt;br /&gt;
cn: %termcom&lt;br /&gt;
sudoUser: %termcom&lt;br /&gt;
sudoHost: honk&lt;br /&gt;
sudoHost: hiss&lt;br /&gt;
sudoHost: gosling&lt;br /&gt;
sudoCommand: ALL&lt;br /&gt;
sudoRunAsUser: ALL&amp;lt;/pre&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt;Now add them:&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# ldapadd -cxWD cn=root,dc=wics,dc=uwaterloo,dc=ca -f sudoers.ldif&lt;br /&gt;
Enter LDAP Password:&lt;br /&gt;
adding new entry &amp;amp;quot;ou=SUDOers,dc=wics,dc=uwaterloo,dc=ca&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
adding new entry &amp;amp;quot;cn=defaults,ou=SUDOers,dc=wics,dc=uwaterloo,dc=ca&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
adding new entry &amp;amp;quot;cn=%syscom,ou=SUDOers,dc=wics,dc=uwaterloo,dc=ca&amp;amp;quot;&lt;br /&gt;
&lt;br /&gt;
adding new entry &amp;amp;quot;cn=%termcom,ou=SUDOers,dc=wics,dc=uwaterloo,dc=ca&amp;amp;quot;&amp;lt;/pre&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt;Last, add some special local groups via [https://git.uwaterloo.ca/wics/documentation/blob/master/ldap/local-groups.ldif &amp;lt;code&amp;gt;local-groups.ldif&amp;lt;/code&amp;gt;]:&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;# ldapadd -cxWD cn=root,dc=wics,dc=uwaterloo,dc=ca -f local-groups.ldif&amp;lt;/pre&amp;gt;&lt;br /&gt;
The local groups are special because they usually are present on all systems, but we want to be able to add users to them at the LDAP level. For instance, the audio group controls access to sound equipment, and the adm group controls log read access.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;p&amp;gt;That&#039;s all the entries we have to add manually! Now we can use software for the rest. See [[weo|&amp;lt;code&amp;gt;ceo&amp;lt;/code&amp;gt;]] for more details.&amp;lt;/p&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Querying LDAP ===&lt;br /&gt;
&lt;br /&gt;
There are many tools available for issuing LDAP queries. Queries should be issued to &amp;lt;tt&amp;gt;ldap1.csclub.uwaterloo.ca&amp;lt;/tt&amp;gt;. The search base you almost certainly want is &amp;lt;tt&amp;gt;dc=csclub,dc=uwaterloo,dc=ca&amp;lt;/tt&amp;gt;. Read access is available without authentication; [[Kerberos]] is used to authenticate commands which require it.&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
&lt;br /&gt;
 ldapsearch -x -H ldap://ldap1.csclub.uwaterloo.ca -b dc=csclub,dc=uwaterloo,dc=ca uid=ctdalek&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;tt&amp;gt;-x&amp;lt;/tt&amp;gt; option causes &amp;lt;tt&amp;gt;ldapsearch&amp;lt;/tt&amp;gt; to switch to simple authentication rather than trying to authenticate via SASL (which will fail if you do not have a Kerberos ticket).&lt;br /&gt;
&lt;br /&gt;
The University LDAP server (uwldap.uwaterloo.ca) can also be queried like this. Again, use &amp;quot;simple authentication&amp;quot; as read access is available (from on campus) without authentication. SASL authentication will fail without additional parameters.&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
&lt;br /&gt;
 ldapsearch -x -H ldap://uwldap.uwaterloo.ca -b dc=uwaterloo,dc=ca &amp;quot;cn=Prabhakar Ragde&amp;quot;&lt;br /&gt;
&lt;br /&gt;
=== Replication ===&lt;br /&gt;
&lt;br /&gt;
While &amp;lt;tt&amp;gt;ldap1.csclub.uwaterloo.ca&amp;lt;/tt&amp;gt; ([[Machine_List#auth1|auth1]]) is the LDAP master, an up-to-date replica is available on &amp;lt;tt&amp;gt;ldap2.csclub.uwaterloo.ca&amp;lt;/tt&amp;gt; ([[Machine_List#auth2|auth2]]).&lt;br /&gt;
&lt;br /&gt;
In order to replicate changes from the master, the slave maintains an authenticated connection to the master which provides it with full read access to all changes.&lt;br /&gt;
&lt;br /&gt;
Specifically, &amp;lt;tt&amp;gt;/etc/systemd/system/k5start-slapd.service&amp;lt;/tt&amp;gt; maintains an active Kerberos ticket for &amp;lt;tt&amp;gt;ldap/auth2.csclub.uwaterloo.ca@CSCLUB.UWATERLOO.CA&amp;lt;/tt&amp;gt; in &amp;lt;tt&amp;gt;/var/run/slapd/krb5cc&amp;lt;/tt&amp;gt;. This is then used to authenticate the slave to the server, who maps this principal to &amp;lt;tt&amp;gt;cn=ldap-slave,dc=csclub,dc=uwaterloo,dc=ca&amp;lt;/tt&amp;gt;, which in turn has full read privileges.&lt;br /&gt;
&lt;br /&gt;
In the event of master failure, all hosts should fail LDAP reads seamlessly over to the slave.&lt;br /&gt;
&lt;br /&gt;
[[Category:Software]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Modifying LDAP entry ===&lt;br /&gt;
&lt;br /&gt;
Editing entries can be easily done with &amp;lt;code&amp;gt;ldapvi&amp;lt;/code&amp;gt;. First search for the entry using &amp;lt;code&amp;gt;ldapsearch&amp;lt;/code&amp;gt; like above, and change &amp;lt;code&amp;gt;ldapsearch -x&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;ldapvi -Y GSSAPI&amp;lt;/code&amp;gt; to make your edits.&lt;br /&gt;
&lt;br /&gt;
Note that if your &amp;lt;tt&amp;gt;EDITOR&amp;lt;/tt&amp;gt; enviroment is set to something not avaliable it will give out errors like&lt;br /&gt;
&lt;br /&gt;
 error (misc.c line 180): No such file or directory&lt;br /&gt;
 editor died&lt;br /&gt;
 error (ldapvi.c line 83): No such file or directory&lt;br /&gt;
&lt;br /&gt;
This can be fixed by something like&lt;br /&gt;
&lt;br /&gt;
 EDITOR=vi ldapvi ******&lt;br /&gt;
&lt;br /&gt;
==== Changing a user&#039;s username ====&lt;br /&gt;
&lt;br /&gt;
Only a member of the Systems Committee can change a user&#039;s username. &#039;&#039;&#039;At all times, a user&#039;s username must match the user&#039;s username in WatIAM.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
All changes to an account MUST be done in person so that identity can be confirmed. If a member cannot attend in person, then an alternate method of identity verification may be chosen by the Systems Administrator.&lt;br /&gt;
&lt;br /&gt;
# Edit entries in LDAP (&amp;lt;code&amp;gt;ldapvi -Y GSSAPI&amp;lt;/code&amp;gt;)&lt;br /&gt;
#* Find and replace the user&#039;s old username with the new one (&amp;lt;code&amp;gt;%s/$OLD/$NEW/g&amp;lt;/code&amp;gt;)&lt;br /&gt;
# Change the user&#039;s Kerberos principal (on auth1 using &amp;lt;code&amp;gt;kadmin&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;renprinc $OLD $NEW&amp;lt;/code&amp;gt;)&lt;br /&gt;
# Move the user&#039;s home directory (on phosphoric-acid, &amp;lt;code&amp;gt;mv /users/$OLD /users/$NEW&amp;lt;/code&amp;gt;)&lt;br /&gt;
# Modify the user&#039;s ~/.forward file if their old username is in it.&lt;br /&gt;
# Change the user&#039;s csc-general (and csc-industry, if subscribed) email address for &amp;lt;code&amp;gt;$OLD@csclub.uwaterloo.ca&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;$NEW@csclub.uwaterloo.ca&amp;lt;/code&amp;gt;&lt;br /&gt;
#* https://mailman.csclub.uwaterloo.ca/admin/csc-general&lt;br /&gt;
# If the user has vhosts on caffeine, update them to point to their new username&lt;br /&gt;
&lt;br /&gt;
If the user&#039;s account has been around for a while, and they request it, forward email from their old username to their new one.&lt;br /&gt;
&lt;br /&gt;
# Edit &amp;lt;code&amp;gt;/etc/aliases&amp;lt;/code&amp;gt; on mail. &amp;lt;code&amp;gt;$OLD: $NEW&amp;lt;/code&amp;gt;&lt;br /&gt;
# Run &amp;lt;code&amp;gt;newaliases&amp;lt;/code&amp;gt;&lt;/div&gt;</summary>
		<author><name>K95ma</name></author>
	</entry>
	<entry>
		<id>https://wiki.csclub.uwaterloo.ca/index.php?title=MediaWiki&amp;diff=5419</id>
		<title>MediaWiki</title>
		<link rel="alternate" type="text/html" href="https://wiki.csclub.uwaterloo.ca/index.php?title=MediaWiki&amp;diff=5419"/>
		<updated>2025-09-10T00:38:29Z</updated>

		<summary type="html">&lt;p&gt;K95ma: f&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;CSC&#039;s MediaWiki instance is located at &amp;lt;code&amp;gt;/var/lib/mediawiki&amp;lt;/code&amp;gt; on [[Machine List#caffeine|caffeine]].&lt;/div&gt;</summary>
		<author><name>K95ma</name></author>
	</entry>
	<entry>
		<id>https://wiki.csclub.uwaterloo.ca/index.php?title=MediaWiki&amp;diff=5418</id>
		<title>MediaWiki</title>
		<link rel="alternate" type="text/html" href="https://wiki.csclub.uwaterloo.ca/index.php?title=MediaWiki&amp;diff=5418"/>
		<updated>2025-09-10T00:37:57Z</updated>

		<summary type="html">&lt;p&gt;K95ma: +&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;CSC&#039;s MediaWiki instance is located at {{code|/var/lib/mediawiki}} on [[Machine List#caffeine|caffeine]].&lt;/div&gt;</summary>
		<author><name>K95ma</name></author>
	</entry>
</feed>