@@ -633,6 +633,227 @@ def test_libpq5_version(host):
633633 print ("✓ libpq5 version is >= 14" )
634634
635635
636+ def test_jit_pam_module_installed (host ):
637+ """Test that the JIT PAM module (pam_jit_pg.so) is properly installed."""
638+ # Check PostgreSQL version first
639+ result = run_ssh_command (
640+ host ["ssh" ], "sudo -u postgres psql --version | grep -oE '[0-9]+' | head -1"
641+ )
642+ pg_major_version = 15 # Default
643+ if result ["succeeded" ] and result ["stdout" ].strip ():
644+ try :
645+ pg_major_version = int (result ["stdout" ].strip ())
646+ except ValueError :
647+ pass
648+
649+ # Skip test for PostgreSQL 15 as gatekeeper is not installed for PG15
650+ if pg_major_version == 15 :
651+ print ("\n Skipping JIT PAM module test for PostgreSQL 15 (not installed)" )
652+ return
653+
654+ # Check if gatekeeper is installed via Nix
655+ result = run_ssh_command (
656+ host ["ssh" ],
657+ "sudo -u postgres ls -la /var/lib/postgresql/.nix-profile/lib/security/pam_jit_pg.so 2>/dev/null" ,
658+ )
659+ if result ["succeeded" ]:
660+ print (f"\n JIT PAM module found in Nix profile:\n { result ['stdout' ]} " )
661+ else :
662+ print ("\n JIT PAM module not found in postgres user's Nix profile" )
663+ assert False , "JIT PAM module (pam_jit_pg.so) not found in expected location"
664+
665+ # Check if the symlink exists in the Linux PAM security directory
666+ result = run_ssh_command (
667+ host ["ssh" ],
668+ "find /nix/store -type f -path '*/lib/security/pam_jit_pg.so' 2>/dev/null | head -5" ,
669+ )
670+ if result ["succeeded" ] and result ["stdout" ].strip ():
671+ print (f"\n JIT PAM module symlinks found:\n { result ['stdout' ]} " )
672+ else :
673+ print ("\n No JIT PAM module symlinks found in /nix/store" )
674+
675+ # Verify the module is a valid shared library
676+ result = run_ssh_command (
677+ host ["ssh" ], "file /var/lib/postgresql/.nix-profile/lib/security/pam_jit_pg.so"
678+ )
679+ if result ["succeeded" ]:
680+ print (f"\n JIT PAM module file type:\n { result ['stdout' ]} " )
681+ assert (
682+ "shared object" in result ["stdout" ].lower ()
683+ or "dynamically linked" in result ["stdout" ].lower ()
684+ ), "JIT PAM module is not a valid shared library"
685+
686+ print ("✓ JIT PAM module is properly installed" )
687+
688+
689+ def test_pam_postgresql_config (host ):
690+ """Test that the PAM configuration for PostgreSQL exists and is properly configured."""
691+ # Check PostgreSQL version to determine if PAM config should exist
692+ result = run_ssh_command (
693+ host ["ssh" ], "sudo -u postgres psql --version | grep -oE '[0-9]+' | head -1"
694+ )
695+ pg_major_version = 15 # Default
696+ if result ["succeeded" ] and result ["stdout" ].strip ():
697+ try :
698+ pg_major_version = int (result ["stdout" ].strip ())
699+ except ValueError :
700+ pass
701+
702+ print (f"\n PostgreSQL major version: { pg_major_version } " )
703+
704+ # PAM config should exist for non-PostgreSQL 15 versions
705+ if pg_major_version != 15 :
706+ # Check if PAM config file exists
707+ result = run_ssh_command (host ["ssh" ], "ls -la /etc/pam.d/postgresql" )
708+ if result ["succeeded" ]:
709+ print (f"\n PAM config file found:\n { result ['stdout' ]} " )
710+
711+ # Check file permissions
712+ result = run_ssh_command (
713+ host ["ssh" ], "stat -c '%a %U %G' /etc/pam.d/postgresql"
714+ )
715+ if result ["succeeded" ]:
716+ perms = result ["stdout" ].strip ()
717+ print (f"PAM config permissions: { perms } " )
718+ # Should be owned by postgres:postgres with 664 permissions
719+ assert (
720+ "postgres postgres" in perms
721+ ), "PAM config not owned by postgres:postgres"
722+ else :
723+ print ("\n PAM config file not found" )
724+ assert False , "PAM configuration file /etc/pam.d/postgresql not found"
725+ else :
726+ print ("\n Skipping PAM config check for PostgreSQL 15" )
727+ # For PostgreSQL 15, the PAM config should NOT exist
728+ result = run_ssh_command (host ["ssh" ], "test -f /etc/pam.d/postgresql" )
729+ if result ["succeeded" ]:
730+ print ("\n WARNING: PAM config exists for PostgreSQL 15 (not expected)" )
731+
732+ print ("✓ PAM configuration is properly set up" )
733+
734+
735+ def test_jit_pam_gatekeeper_profile (host ):
736+ """Test that the gatekeeper package is properly installed in the postgres user's Nix profile."""
737+ # Check PostgreSQL version first
738+ result = run_ssh_command (
739+ host ["ssh" ], "sudo -u postgres psql --version | grep -oE '[0-9]+' | head -1"
740+ )
741+ pg_major_version = 15 # Default
742+ if result ["succeeded" ] and result ["stdout" ].strip ():
743+ try :
744+ pg_major_version = int (result ["stdout" ].strip ())
745+ except ValueError :
746+ pass
747+
748+ # Skip test for PostgreSQL 15 as gatekeeper is not installed for PG15
749+ if pg_major_version == 15 :
750+ print ("\n Skipping gatekeeper profile test for PostgreSQL 15 (not installed)" )
751+ return
752+
753+ # Check if gatekeeper is in the postgres user's Nix profile
754+ result = run_ssh_command (
755+ host ["ssh" ],
756+ "sudo -u postgres nix profile list --json | jq -r '.elements.gatekeeper.storePaths[0]'" ,
757+ )
758+ if result ["succeeded" ] and result ["stdout" ].strip ():
759+ print (f"\n Gatekeeper found in Nix profile:\n { result ['stdout' ]} " )
760+ else :
761+ # Try alternative check
762+ result = run_ssh_command (
763+ host ["ssh" ],
764+ "sudo -u postgres ls -la /var/lib/postgresql/.nix-profile/ | grep -i gate" ,
765+ )
766+ if result ["succeeded" ] and result ["stdout" ].strip ():
767+ print (f"\n Gatekeeper-related files in profile:\n { result ['stdout' ]} " )
768+ else :
769+ print ("\n Gatekeeper not found in postgres user's Nix profile" )
770+ # This might be expected if it's installed system-wide instead
771+
772+ # Check if we can find the gatekeeper derivation
773+ result = run_ssh_command (
774+ host ["ssh" ],
775+ "find /nix/store -maxdepth 1 -type d -name '*gatekeeper*' 2>/dev/null | head -5" ,
776+ )
777+ if result ["succeeded" ] and result ["stdout" ].strip ():
778+ print (f"\n Gatekeeper derivations found:\n { result ['stdout' ]} " )
779+ else :
780+ print ("\n No gatekeeper derivations found in /nix/store" )
781+
782+ print ("✓ Gatekeeper package installation check completed" )
783+
784+
785+ def test_jit_pam_module_dependencies (host ):
786+ """Test that the JIT PAM module has all required dependencies."""
787+ # Check PostgreSQL version first
788+ result = run_ssh_command (
789+ host ["ssh" ], "sudo -u postgres psql --version | grep -oE '[0-9]+' | head -1"
790+ )
791+ pg_major_version = 15 # Default
792+ if result ["succeeded" ] and result ["stdout" ].strip ():
793+ try :
794+ pg_major_version = int (result ["stdout" ].strip ())
795+ except ValueError :
796+ pass
797+
798+ # Skip test for PostgreSQL 15 as gatekeeper is not installed for PG15
799+ if pg_major_version == 15 :
800+ print (
801+ "\n Skipping JIT PAM module dependencies test for PostgreSQL 15 (not installed)"
802+ )
803+ return
804+
805+ # Check dependencies of the PAM module
806+ result = run_ssh_command (
807+ host ["ssh" ],
808+ "ldd /var/lib/postgresql/.nix-profile/lib/security/pam_jit_pg.so 2>/dev/null" ,
809+ )
810+ if result ["succeeded" ]:
811+ print (f"\n JIT PAM module dependencies:\n { result ['stdout' ]} " )
812+
813+ # Check for required libraries
814+ required_libs = ["libpam" , "libc" ]
815+ for lib in required_libs :
816+ if lib not in result ["stdout" ].lower ():
817+ print (f"WARNING: Required library { lib } not found in dependencies" )
818+
819+ # Check for any missing dependencies
820+ if "not found" in result ["stdout" ].lower ():
821+ assert False , "JIT PAM module has missing dependencies"
822+ else :
823+ print ("\n Could not check JIT PAM module dependencies" )
824+
825+ print ("✓ JIT PAM module dependencies are satisfied" )
826+
827+
828+ def test_jit_pam_postgresql_integration (host ):
829+ """Test that PostgreSQL can be configured to use PAM authentication."""
830+ # Check if PAM is available as an authentication method in PostgreSQL
831+ result = run_ssh_command (
832+ host ["ssh" ],
833+ "sudo -u postgres psql -c \" SELECT name, setting FROM pg_settings WHERE name LIKE '%pam%';\" 2>/dev/null" ,
834+ )
835+ if result ["succeeded" ]:
836+ print (f"\n PostgreSQL PAM-related settings:\n { result ['stdout' ]} " )
837+
838+ # Check pg_hba.conf for potential PAM entries (even if not currently active)
839+ result = run_ssh_command (
840+ host ["ssh" ],
841+ "grep -i pam /etc/postgresql/pg_hba.conf 2>/dev/null || echo 'No PAM entries in pg_hba.conf'" ,
842+ )
843+ if result ["succeeded" ]:
844+ print (f"\n PAM entries in pg_hba.conf:\n { result ['stdout' ]} " )
845+
846+ # Verify PostgreSQL was compiled with PAM support
847+ result = run_ssh_command (
848+ host ["ssh" ],
849+ "sudo -u postgres pg_config --configure 2>/dev/null | grep -i pam || echo 'PAM compile flag not found'" ,
850+ )
851+ if result ["succeeded" ]:
852+ print (f"\n PostgreSQL PAM compile flags:\n { result ['stdout' ]} " )
853+
854+ print ("✓ PostgreSQL PAM integration check completed" )
855+
856+
636857def test_postgrest_read_only_session_attrs (host ):
637858 """Test PostgREST with target_session_attrs=read-only and check for session errors."""
638859 # First, check if PostgreSQL is configured for read-only mode
0 commit comments