You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@airavata.apache.org by la...@apache.org on 2026/09/11 19:39:27 UTC
(airavata-custos) branch pam-logo created (now a8fdc5740)
This is an automated email from the ASF dual-hosted git repository.
lahirujayathilake pushed a change to branch pam-logo
in repository https://gitbox.apache.org/repos/asf/airavata-custos.git
at a8fdc5740 Fix SELinux blocking SSH device login on Rocky 10
This branch includes the following new commits:
new 708e4f953 Redesign the device login prompt and make the logo art configurable
new bf4f84f9a Build the PAM module from extensions and deploy the prompt logo art
new a8fdc5740 Fix SELinux blocking SSH device login on Rocky 10
The 3 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails. The revisions
listed as "add" were already present in the repository and have only
been added to this reference.
(airavata-custos) 02/03: Build the PAM module from extensions and deploy the prompt logo art
Posted by la...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
lahirujayathilake pushed a commit to branch pam-logo
in repository https://gitbox.apache.org/repos/asf/airavata-custos.git
commit bf4f84f9ac7dad8c97d7ee4f26452c489424bc63
Author: lahiruj <la...@gmail.com>
AuthorDate: Fri Sep 11 14:39:32 2026 -0400
Build the PAM module from extensions and deploy the prompt logo art
---
dev-ops/account-provisioning/enroll-node.yml | 17 ++++++++++++++---
dev-ops/account-provisioning/group_vars/all.yml.example | 17 ++++++++++++++---
.../templates/pam-oauth2-config.json.j2 | 7 ++++++-
3 files changed, 34 insertions(+), 7 deletions(-)
diff --git a/dev-ops/account-provisioning/enroll-node.yml b/dev-ops/account-provisioning/enroll-node.yml
index 5a2075645..d0d83663f 100644
--- a/dev-ops/account-provisioning/enroll-node.yml
+++ b/dev-ops/account-provisioning/enroll-node.yml
@@ -188,21 +188,22 @@
state: present
tags: [pam]
- - name: "[pam] Clone pam_oauth2_device repository"
+ - name: "[pam] Clone pam_oauth2_device source"
git:
repo: "{{ pam_oauth2_device_repo }}"
dest: "{{ pam_oauth2_device_src_dir }}"
+ version: "{{ pam_oauth2_device_version | default('master') }}"
force: true
tags: [pam]
- name: "[pam] Build pam_oauth2_device"
make:
- chdir: "{{ pam_oauth2_device_src_dir }}"
+ chdir: "{{ pam_oauth2_device_build_dir }}"
tags: [pam]
- name: "[pam] Install pam_oauth2_device.so"
copy:
- src: "{{ pam_oauth2_device_src_dir }}/pam_oauth2_device.so"
+ src: "{{ pam_oauth2_device_build_dir }}/pam_oauth2_device.so"
dest: /usr/lib64/security/pam_oauth2_device.so
remote_src: true
owner: root
@@ -219,6 +220,16 @@
mode: "0755"
tags: [pam]
+ - name: "[pam] Copy custom logo art"
+ copy:
+ src: "{{ pam_logo_file_src }}"
+ dest: /etc/pam_oauth2_device/logo.txt
+ owner: root
+ group: root
+ mode: "0644"
+ when: pam_logo_file_src is defined
+ tags: [pam]
+
- name: "[pam] Template pam_oauth2_device config.json"
template:
src: templates/pam-oauth2-config.json.j2
diff --git a/dev-ops/account-provisioning/group_vars/all.yml.example b/dev-ops/account-provisioning/group_vars/all.yml.example
index 9618de85a..3a60e69ab 100644
--- a/dev-ops/account-provisioning/group_vars/all.yml.example
+++ b/dev-ops/account-provisioning/group_vars/all.yml.example
@@ -65,10 +65,14 @@ sssd_domain_name: "xxx"
# -----------------------------------------------------------------------------
# Git repo to clone for building pam_oauth2_device
-pam_oauth2_device_repo: "https://github.com/cyber-shuttle/pam_oauth2_device.git"
+pam_oauth2_device_repo: "https://github.com/apache/airavata-custos.git"
-# Where to clone the source on the target
-pam_oauth2_device_src_dir: "/usr/local/src/pam_oauth2_device"
+# Branch or tag to build from
+pam_oauth2_device_version: "master"
+
+# Where to clone the source on the target, and where the module builds
+pam_oauth2_device_src_dir: "/usr/local/src/pam_oauth2_device/airavata-custos"
+pam_oauth2_device_build_dir: "{{ pam_oauth2_device_src_dir }}/extensions/CILogon-SSH-PAM"
# LDAP settings for pam_oauth2_device user lookup
# This lets the PAM module verify the SSH username exists in LDAP
@@ -82,6 +86,13 @@ pam_oauth2_ldap_attr: "uid"
# Enable debug logging in pam_oauth2_device (set false for production)
pam_oauth2_debug: false
+# System name shown in the SSH sign-in prompt (defaults to the inventory hostname)
+# pam_prompt_hostname: "login.cluster.example.org"
+
+# Local text file shown in place of the built-in logo art in the sign-in prompt.
+# Copied to /etc/pam_oauth2_device/logo.txt on the node; leave unset for the default art.
+# pam_logo_file_src: "~/cluster-logo.txt"
+
# -----------------------------------------------------------------------------
# Validation
# -----------------------------------------------------------------------------
diff --git a/dev-ops/account-provisioning/templates/pam-oauth2-config.json.j2 b/dev-ops/account-provisioning/templates/pam-oauth2-config.json.j2
index 8ff3a0dad..14be7337a 100644
--- a/dev-ops/account-provisioning/templates/pam-oauth2-config.json.j2
+++ b/dev-ops/account-provisioning/templates/pam-oauth2-config.json.j2
@@ -41,6 +41,11 @@
"filter": "{{ pam_oauth2_ldap_filter }}",
"attr": "{{ pam_oauth2_ldap_attr }}"
},
- "qr": { "show": false, "error_correction_level": 1 },
+ "qr": { "error_correction_level": 0 },
+ "prompt": {
+ "hostname": "{{ pam_prompt_hostname | default(inventory_hostname) }}"{% if pam_logo_file_src is defined %},
+ "logo_file": "/etc/pam_oauth2_device/logo.txt"
+{% endif %}
+ },
"tls": { "ca_bundle": "{{ ldap_tls_cacert }}" }
}
(airavata-custos) 01/03: Redesign the device login prompt and make the logo art configurable
Posted by la...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
lahirujayathilake pushed a commit to branch pam-logo
in repository https://gitbox.apache.org/repos/asf/airavata-custos.git
commit 708e4f953c472e8e31694ecb5e550183654462a3
Author: lahiruj <la...@gmail.com>
AuthorDate: Fri Sep 11 14:32:57 2026 -0400
Redesign the device login prompt and make the logo art configurable
---
extensions/CILogon-SSH-PAM/README.md | 4 +
extensions/CILogon-SSH-PAM/config_template.json | 4 +
extensions/CILogon-SSH-PAM/src/include/config.cpp | 2 +
extensions/CILogon-SSH-PAM/src/include/config.hpp | 4 +-
.../CILogon-SSH-PAM/src/pam_oauth2_device.cpp | 90 +++++++++++++++++-----
.../CILogon-SSH-PAM/src/pam_oauth2_device.hpp | 3 +-
extensions/CILogon-SSH-PAM/test/unit.cpp | 49 +++++++++++-
7 files changed, 133 insertions(+), 23 deletions(-)
diff --git a/extensions/CILogon-SSH-PAM/README.md b/extensions/CILogon-SSH-PAM/README.md
index 41828cfac..5416e947b 100644
--- a/extensions/CILogon-SSH-PAM/README.md
+++ b/extensions/CILogon-SSH-PAM/README.md
@@ -152,6 +152,9 @@ Thus, at the top level, there is a single object with a number of entries, descr
| tls | ca\_path | String | N | Directory with trust anchors | Note 2 |
| qr | | Object | N | | |
| qr | error\_correction\_level | Int | Y | QR code | Note 3 |
+| prompt | | Object | N | | |
+| prompt | hostname | String | N | System name shown in the sign-in prompt | Note 5 |
+| prompt | logo\_file | String | N | Text file replacing the built-in logo art | Note 5 |
Notes:
@@ -162,6 +165,7 @@ Notes:
* If both ca\_path and ca\_bundle are present, the latter takes precedence
3 The QR code section is optional but if present, it must have the error correction level defined. Permitted values are 1 (low), 2 (medium), 3 (high) or -1 (disabled). If the section is missing, the QR code is disabled.
4 The "${url}" above would be the URL (hostname) of your OpenID Provider. Its host certificate must be valid when checked against the CA bundle (see item 2)
+5 The prompt section controls the sign-in message shown over SSH. "hostname" is the system name presented to the user; if unset, "custos-cluster" is shown. "logo\_file" names a plain-text file whose content is shown in place of the built-in logo art at the top of the prompt, so a site can show its own branding without rebuilding the module; if unset or unreadable, the built-in art is shown. The config is read on every login, so changing the file takes effect immediately. The prompt (log [...]
#### Table 2: Configuring Authorisation Flow
diff --git a/extensions/CILogon-SSH-PAM/config_template.json b/extensions/CILogon-SSH-PAM/config_template.json
index a3bad1ef5..56dd6b221 100644
--- a/extensions/CILogon-SSH-PAM/config_template.json
+++ b/extensions/CILogon-SSH-PAM/config_template.json
@@ -25,6 +25,10 @@
"qr": {
"error_correction_level": 0
},
+ "prompt": {
+ "hostname": "cluster.example.org",
+ "logo_file": "/etc/pam_oauth2_device/logo.txt"
+ },
"group": {
"access": true,
"service_name": "stfc-cloud-prod"
diff --git a/extensions/CILogon-SSH-PAM/src/include/config.cpp b/extensions/CILogon-SSH-PAM/src/include/config.cpp
index 3ca87f677..6348a4d53 100644
--- a/extensions/CILogon-SSH-PAM/src/include/config.cpp
+++ b/extensions/CILogon-SSH-PAM/src/include/config.cpp
@@ -149,6 +149,8 @@ Config::load(const char *path)
variable({"oauth","username_attribute"},value(""),true, username_attribute),
variable({"oauth","local_username_suffix"},value(""),false, local_username_suffix),
variable({"qr","error_correction_level"},value(-1),false, qr_error_correction_level),
+ variable({"prompt","hostname"},value(""),false, prompt_hostname),
+ variable({"prompt","logo_file"},value(""),false, prompt_logo_file),
variable({"client_debug"},value(false),false, client_debug),
variable({"http_basic_auth"},value(true),false, http_basic_auth),
variable({"cloud","access"},value(false),false, cloud_access),
diff --git a/extensions/CILogon-SSH-PAM/src/include/config.hpp b/extensions/CILogon-SSH-PAM/src/include/config.hpp
index 5a591d30a..f9960ad6e 100644
--- a/extensions/CILogon-SSH-PAM/src/include/config.hpp
+++ b/extensions/CILogon-SSH-PAM/src/include/config.hpp
@@ -30,7 +30,9 @@ public:
cloud_endpoint,
cloud_username,
local_username_suffix,
- metadata_file;
+ metadata_file,
+ prompt_hostname,
+ prompt_logo_file;
int qr_error_correction_level;
bool group_access,
cloud_access,
diff --git a/extensions/CILogon-SSH-PAM/src/pam_oauth2_device.cpp b/extensions/CILogon-SSH-PAM/src/pam_oauth2_device.cpp
index 4d8913bf4..959395d37 100644
--- a/extensions/CILogon-SSH-PAM/src/pam_oauth2_device.cpp
+++ b/extensions/CILogon-SSH-PAM/src/pam_oauth2_device.cpp
@@ -129,29 +129,75 @@ std::string getQr(const char *text, const int ecc = 0, const int border = 1)
return oss.str();
}
-std::string DeviceAuthResponse::get_prompt(const int qr_ecc = 0)
+static const char lockup[] =
+ " ▄▄███████\n"
+ " ▄██████████\n"
+ " ▄████████████\n"
+ " ▀▀█████▀▀▀▀█\n"
+ " ▄▄██▄▄▀▀ ▄█▀▀▀█▄ ██ ██ ▄█▀▀▀█▄ ███████ ▄█▀▀▀█▄ ▄█▀▀▀█▄\n"
+ "████████ ██ ██ ██ ▀█▄▄▄▄ ███ ██ ██ ▀█▄▄▄▄\n"
+ "████████ ██ ▄▄ ██ ██ ▄▄ ██ ███ ██ ██ ▄▄ ██\n"
+ " ▀▀██▀▀▄▄ ▀▀▀▀▀ ▀▀▀▀▀ ▀▀▀▀▀ ▀▀▀ ▀▀▀▀▀ ▀▀▀▀▀\n"
+ " ▄▄█████▄▄▄▄█\n"
+ " ▀████████████\n"
+ " ▀██████████\n"
+ " ▀▀███████\n";
+
+// Printable characters only in the prompt. The SSH client escapes control
+// bytes in auth messages, so ANSI color codes come out as literal text.
+
+static std::string load_logo(Config const &config)
+{
+ if (!config.prompt_logo_file.empty())
+ {
+ std::ifstream file(config.prompt_logo_file);
+ std::ostringstream art;
+ art << file.rdbuf();
+ if (file && !art.str().empty())
+ {
+ std::string logo = art.str();
+ if (logo.back() != '\n')
+ logo += '\n';
+ return logo;
+ }
+ }
+ return lockup;
+}
+
+std::string DeviceAuthResponse::get_prompt(Config const &config)
{
bool complete_url = !verification_uri_complete.empty();
+ std::string const &link = complete_url ? verification_uri_complete : verification_uri;
+
+ std::string host = config.prompt_hostname.empty() ? "custos-cluster" : config.prompt_hostname;
+
std::ostringstream prompt;
- prompt << "Authenticate at\n-----------------\n"
- << (complete_url ? verification_uri_complete : verification_uri)
- << "\n-----------------\n";
- if (!complete_url)
+ // the SSH client prints its own "(user@host)" prefix on the first line;
+ // a leading blank line separates it out from the artwork
+ prompt << "\n";
+ prompt << load_logo(config);
+ prompt << "\n"
+ << "Sign in to " << host << " to finish connecting.\n\n"
+ << " Open this link in a browser\n"
+ << " " << link << "\n";
+
+ std::string note = complete_url
+ ? "The page will show " + user_code + "."
+ : "Enter code " + user_code + " when asked.";
+ if (expires_in >= 60)
+ note += " Expires in " + std::to_string(expires_in / 60) + " min.";
+ prompt << " " << note << "\n";
+
+ if (config.qr_error_correction_level >= 0)
{
- prompt << "With code " << user_code
- << "\n-----------------\n";
+ prompt << "\n Or scan this code with your phone\n\n";
+ std::istringstream qr(getQr(link.c_str(), config.qr_error_correction_level));
+ std::string row;
+ while (std::getline(qr, row))
+ prompt << " " << row << "\n";
}
- if (qr_ecc >= 0) {
- prompt << "Or scan the QR code to authenticate with a mobile device"
- << std::endl
- << std::endl
- << getQr((complete_url ? verification_uri_complete : verification_uri).c_str(), qr_ecc)
- << std::endl
- << "Hit enter when you have finished authenticating\n";
- } else {
- prompt << "Hit enter when you have finished authenticating\n";
- }
+ prompt << "\nPress Enter once you've signed in, or Ctrl-C to cancel.\n";
return prompt.str();
}
@@ -182,6 +228,10 @@ void make_authorization_request(const Config &config,
{
response->verification_uri_complete = data.at("verification_uri_complete");
}
+ if (data.find("expires_in") != data.end() && data.at("expires_in").is_number())
+ {
+ response->expires_in = data.at("expires_in");
+ }
}
catch (json::exception &e)
{
@@ -291,7 +341,7 @@ get_userinfo(const Config &config,
}
void show_prompt(pam_handle_t *pamh,
- int qr_error_correction_level,
+ Config const &config,
DeviceAuthResponse *device_auth_response)
{
int pam_err;
@@ -305,7 +355,7 @@ void show_prompt(pam_handle_t *pamh,
pam_err = pam_get_item(pamh, PAM_CONV, (const void **)&conv);
if (pam_err != PAM_SUCCESS)
throw PamError("Prompt: failed to get PAM_CONV");
- prompt = device_auth_response->get_prompt(qr_error_correction_level);
+ prompt = device_auth_response->get_prompt(config);
msg.msg_style = PAM_PROMPT_ECHO_OFF;
msg.msg = prompt.c_str();
msgp = &msg;
@@ -508,7 +558,7 @@ PAM_EXTERN int pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, cons
config.client_id, config.client_secret,
config.scope, config.device_endpoint,
&device_auth_response);
- show_prompt(pamh, config.qr_error_correction_level, &device_auth_response);
+ show_prompt(pamh, config, &device_auth_response);
poll_for_token(config, logger,
config.client_id, config.client_secret,
config.token_endpoint,
diff --git a/extensions/CILogon-SSH-PAM/src/pam_oauth2_device.hpp b/extensions/CILogon-SSH-PAM/src/pam_oauth2_device.hpp
index 706e8c8e9..02367d704 100644
--- a/extensions/CILogon-SSH-PAM/src/pam_oauth2_device.hpp
+++ b/extensions/CILogon-SSH-PAM/src/pam_oauth2_device.hpp
@@ -52,7 +52,8 @@ public:
verification_uri,
verification_uri_complete,
device_code;
- std::string get_prompt(const int qr_ecc);
+ int expires_in = 0;
+ std::string get_prompt(Config const &config);
};
void make_authorization_request(Config const &config,
diff --git a/extensions/CILogon-SSH-PAM/test/unit.cpp b/extensions/CILogon-SSH-PAM/test/unit.cpp
index 4afec11f9..10174b49e 100644
--- a/extensions/CILogon-SSH-PAM/test/unit.cpp
+++ b/extensions/CILogon-SSH-PAM/test/unit.cpp
@@ -70,7 +70,7 @@ Userinfo get_userinfo(Config const &config,
std::string const &username_attribute);
void show_prompt(pam_handle_t *pamh,
- int qr_error_correction_level,
+ Config const &config,
DeviceAuthResponse *device_auth_response);
bool is_authorized(Config const &config,
@@ -94,6 +94,53 @@ EXPECT_EQ(cmp_file_string("data/qr2.1.txt", getQr(text, 1, 1)), -1);
EXPECT_EQ(cmp_file_string("data/qr2.2.txt", getQr(text, 2, 1)), -1);
}
+TEST(PamOAuth2Unit, PromptLayout)
+{
+ Config config;
+ config.qr_error_correction_level = 0;
+ config.prompt_hostname = "testhost";
+
+ DeviceAuthResponse response;
+ response.user_code = "XPN-VTQ-NJP";
+ response.verification_uri = "https://cilogon.org/device/";
+ response.verification_uri_complete = "https://cilogon.org/device/?user_code=XPN-VTQ-NJP";
+ response.expires_in = 900;
+
+ std::string prompt = response.get_prompt(config);
+EXPECT_NE(prompt.find("Sign in to testhost to finish connecting."), std::string::npos);
+EXPECT_NE(prompt.find(" Open this link in a browser\n https://cilogon.org/device/?user_code=XPN-VTQ-NJP\n"), std::string::npos);
+EXPECT_NE(prompt.find("The page will show XPN-VTQ-NJP. Expires in 15 min."), std::string::npos);
+EXPECT_NE(prompt.find(" Or scan this code with your phone"), std::string::npos);
+EXPECT_NE(prompt.find("Press Enter once you've signed in, or Ctrl-C to cancel."), std::string::npos);
+// 40 rows total, so on a 24-row terminal only the lockup scrolls off
+EXPECT_EQ(std::count(prompt.begin(), prompt.end(), '\n'), 40);
+// the SSH client shows control bytes literally, so none may be emitted
+EXPECT_EQ(prompt.find('\033'), std::string::npos);
+
+ response.verification_uri_complete.clear();
+EXPECT_NE(response.get_prompt(config).find("Enter code XPN-VTQ-NJP when asked."), std::string::npos);
+}
+
+TEST(PamOAuth2Unit, PromptLogoFile)
+{
+ Config config;
+ config.qr_error_correction_level = -1;
+
+ DeviceAuthResponse response;
+ response.user_code = "XPN-VTQ-NJP";
+ response.verification_uri = "https://cilogon.org/device/";
+
+ TempFile logo("SITE ART LINE 1\nSITE ART LINE 2");
+ config.prompt_logo_file = logo.filename();
+ std::string prompt = response.get_prompt(config);
+EXPECT_NE(prompt.find("SITE ART LINE 1\nSITE ART LINE 2\n"), std::string::npos);
+EXPECT_EQ(prompt.find("████████"), std::string::npos);
+
+ // missing file falls back to the built-in art
+ config.prompt_logo_file = "/nonexistent/logo.txt";
+EXPECT_NE(response.get_prompt(config).find("████████"), std::string::npos);
+}
+
TEST(PamOAuth2Unit, IsAuthorized)
{
// Userinfo contains the remote username. Note the suffix is (or will be) configured as ".test" to match a local username "fred"
(airavata-custos) 03/03: Fix SELinux blocking SSH device login on Rocky 10
Posted by la...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
lahirujayathilake pushed a commit to branch pam-logo
in repository https://gitbox.apache.org/repos/asf/airavata-custos.git
commit a8fdc574065253dbf1cee84c574b313ff9675230
Author: lahiruj <la...@gmail.com>
AuthorDate: Fri Sep 11 15:30:14 2026 -0400
Fix SELinux blocking SSH device login on Rocky 10
---
dev-ops/account-provisioning/enroll-node.yml | 3 ++-
.../account-provisioning/files/pam_oauth2_sshd.te | 30 +++++++++++++++++-----
2 files changed, 26 insertions(+), 7 deletions(-)
diff --git a/dev-ops/account-provisioning/enroll-node.yml b/dev-ops/account-provisioning/enroll-node.yml
index d0d83663f..0352a5feb 100644
--- a/dev-ops/account-provisioning/enroll-node.yml
+++ b/dev-ops/account-provisioning/enroll-node.yml
@@ -281,6 +281,7 @@
src: files/pam_oauth2_sshd.te
dest: /tmp/pam_oauth2_sshd.te
mode: "0644"
+ register: selinux_te_copy
tags: [pam]
- name: "[pam] Check if pam_oauth2_sshd SELinux module is already loaded"
@@ -295,7 +296,7 @@
checkmodule -M -m -o /tmp/pam_oauth2_sshd.mod /tmp/pam_oauth2_sshd.te
semodule_package -o /tmp/pam_oauth2_sshd.pp -m /tmp/pam_oauth2_sshd.mod
semodule -i /tmp/pam_oauth2_sshd.pp
- when: selinux_module_check.rc != 0
+ when: selinux_te_copy.changed or selinux_module_check.rc != 0
tags: [pam]
# =========================================================================
diff --git a/dev-ops/account-provisioning/files/pam_oauth2_sshd.te b/dev-ops/account-provisioning/files/pam_oauth2_sshd.te
index 706b1b91a..ae3b65ce2 100644
--- a/dev-ops/account-provisioning/files/pam_oauth2_sshd.te
+++ b/dev-ops/account-provisioning/files/pam_oauth2_sshd.te
@@ -15,12 +15,19 @@
# specific language governing permissions and limitations
# under the License.
-# SELinux type enforcement policy: allow sshd to make HTTPS connections
+# SELinux type enforcement policy: allow sshd to reach the OIDC provider
+# and LDAP.
#
-# Problem: pam_oauth2_device runs inside sshd (sshd_t context) and uses
-# libcurl to contact CILogon over HTTPS (port 443). SELinux's default
-# policy for sshd_t does not permit outbound TCP connections to http_port_t.
-# Without this module, auth silently falls through to password-auth.
+# Problem: pam_oauth2_device runs inside sshd and uses libcurl for the
+# device flow (HTTPS, port 443) plus a direct LDAP query (port 636).
+# SELinux's default sshd policy does not permit those outbound TCP
+# connections. Without this module, auth silently falls through to
+# password-auth.
+#
+# On OpenSSH 9.8+ (Rocky/RHEL 10) authentication runs in the split
+# sshd-session process under its own sshd_session_t type, so that type
+# needs the same allows. It does not exist on RHEL 9, hence the
+# optional block.
#
# Discovered from AVC denial:
# avc: denied { name_connect } for comm="sshd" dest=443
@@ -33,13 +40,24 @@
# semodule_package -o pam_oauth2_sshd.pp -m pam_oauth2_sshd.mod
# semodule -i pam_oauth2_sshd.pp
-module pam_oauth2_sshd 1.0;
+module pam_oauth2_sshd 2.0;
require {
type sshd_t;
type http_port_t;
+ type ldap_port_t;
class tcp_socket name_connect;
}
#============= sshd_t ==============
allow sshd_t http_port_t:tcp_socket name_connect;
+allow sshd_t ldap_port_t:tcp_socket name_connect;
+
+#============= sshd_session_t (OpenSSH 9.8+ split sshd) ==============
+optional {
+ require {
+ type sshd_session_t;
+ }
+ allow sshd_session_t http_port_t:tcp_socket name_connect;
+ allow sshd_session_t ldap_port_t:tcp_socket name_connect;
+}