mirror of
https://salsa.debian.org/freeipa-team/freeipa.git
synced 2024-12-22 23:23:30 -06:00
ccache_sweeper: Add gssproxy service
The usage of the existing gssproxy service(`service/ipa-api`) leads to undesirable for this case side effects such as auto renew of expired credentials. Fixes: https://pagure.io/freeipa/issue/8735 Signed-off-by: Stanislav Levin <slev@altlinux.org> Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com>
This commit is contained in:
parent
216720af83
commit
271fd162a7
@ -16,3 +16,10 @@
|
||||
allow_client_ccache_sync = true
|
||||
cred_usage = initiate
|
||||
euid = $IPAAPI_USER
|
||||
|
||||
[service/ipa-sweeper]
|
||||
mechs = krb5
|
||||
cred_store = keytab:$HTTP_KEYTAB
|
||||
socket = $SWEEPER_SOCKET
|
||||
euid = $IPAAPI_USER
|
||||
cred_usage = initiate
|
||||
|
@ -17,31 +17,39 @@ import stat
|
||||
import sys
|
||||
import time
|
||||
|
||||
from gssapi.raw import acquire_cred_from
|
||||
from ipalib.krb_utils import get_credentials_if_valid
|
||||
from ipaplatform.paths import paths
|
||||
|
||||
|
||||
# process file as a ccache and indicate whether it is expired
|
||||
def should_delete(fname, t, minlife):
|
||||
"""Process file as a ccache and indicate whether it is expired"""
|
||||
# skip directories and other non-files
|
||||
st = os.stat(fname)
|
||||
if not stat.S_ISREG(st.st_mode):
|
||||
return False
|
||||
|
||||
# ignore files that are newer than minlife minutes
|
||||
if t - st.st_mtime < minlife * 60:
|
||||
return False
|
||||
|
||||
# gssproxy inquires input credentials. If they are expired
|
||||
# then gssproxy acquires creds from cred_store according to
|
||||
# the configuration of gssproxy's service, which in this case
|
||||
# hasn't cred_store(besides `keytab:`, used for decryption of
|
||||
# ccache). If there is no ccache within cred_store then gssproxy
|
||||
# adds its own one("MEMORY:internal_%d"), which hasn't
|
||||
# any credentials, thus, scan_ccache fails with KRB5_FCC_NOFILE.
|
||||
# Since the caller requires INITIATE-ONLY and the client keytab
|
||||
# is not provided in cred_store the result of gss_acquire_cred_from
|
||||
# is KRB5_FCC_NOFILE, which is mapped by gssproxy to
|
||||
# 0x04200000 + KRB5_FCC_NOFILE.
|
||||
try:
|
||||
# skip directories and other non-files
|
||||
st = os.stat(fname)
|
||||
if not stat.S_ISREG(st.st_mode):
|
||||
return False
|
||||
creds = get_credentials_if_valid(ccache_name=fname)
|
||||
return creds is None
|
||||
except ValueError:
|
||||
return True
|
||||
|
||||
# ignore files that are newer than minlife minutes
|
||||
if t - st.st_mtime < minlife * 60:
|
||||
return False
|
||||
|
||||
creds = acquire_cred_from({b"ccache": fname.encode("UTF-8")})
|
||||
except FileNotFoundError:
|
||||
# someone else did the work for us
|
||||
return False
|
||||
except Exception as e:
|
||||
print("Not deleting %s due to error %s" % (fname, e))
|
||||
return False
|
||||
|
||||
return creds.lifetime == 0
|
||||
return False
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
@ -52,7 +60,8 @@ if __name__ == "__main__":
|
||||
args = parser.parse_args()
|
||||
|
||||
os.environ["GSS_USE_PROXY"] = "yes"
|
||||
os.environ["GSSPROXY_BEHAVIOR"] = "REMOTE_FIRST"
|
||||
os.environ["GSSPROXY_BEHAVIOR"] = "REMOTE_ONLY"
|
||||
os.environ["GSSPROXY_SOCKET"] = paths.IPA_CCACHE_SWEEPER_GSSPROXY_SOCK
|
||||
|
||||
print("Running sweeper...")
|
||||
|
||||
@ -60,8 +69,12 @@ if __name__ == "__main__":
|
||||
|
||||
os.chdir(paths.IPA_CCACHES)
|
||||
for fname in os.listdir(paths.IPA_CCACHES):
|
||||
if should_delete(fname, t, args.minlife):
|
||||
os.unlink(fname)
|
||||
try:
|
||||
if should_delete(fname, t, args.minlife):
|
||||
os.unlink(fname)
|
||||
except FileNotFoundError:
|
||||
# someone else did the work for us
|
||||
pass
|
||||
|
||||
print("Sweeper finished successfully!")
|
||||
sys.exit(0)
|
||||
|
@ -39,6 +39,12 @@ KRB5_FCC_PERM = 2529639106 # Credentials cache permissions inc
|
||||
KRB5_CC_FORMAT = 2529639111 # Bad format in credentials cache
|
||||
KRB5_REALM_CANT_RESOLVE = 2529639132 # Cannot resolve network address for KDC in requested realm
|
||||
|
||||
# mechglue/gss_plugin.c: #define MAP_ERROR_BASE 0x04200000
|
||||
GSSPROXY_MAP_ERROR_BASE = 69206016
|
||||
|
||||
# GSSProxy error codes
|
||||
GSSPROXY_KRB5_FCC_NOFILE = GSSPROXY_MAP_ERROR_BASE + KRB5_FCC_NOFILE
|
||||
|
||||
krb_ticket_expiration_threshold = 60*5 # number of seconds to accmodate clock skew
|
||||
krb5_time_fmt = '%m/%d/%y %H:%M:%S'
|
||||
ccache_name_re = re.compile(r'^((\w+):)?(.+)')
|
||||
@ -146,7 +152,9 @@ def get_credentials(name=None, ccache_name=None):
|
||||
try:
|
||||
return gssapi.Credentials(usage='initiate', name=name, store=store)
|
||||
except gssapi.exceptions.GSSError as e:
|
||||
if e.min_code == KRB5_FCC_NOFILE: # pylint: disable=no-member
|
||||
if e.min_code in ( # pylint: disable=no-member
|
||||
KRB5_FCC_NOFILE, GSSPROXY_KRB5_FCC_NOFILE,
|
||||
):
|
||||
raise ValueError('"%s", ccache="%s"' % (e, ccache_name))
|
||||
raise
|
||||
|
||||
|
@ -451,6 +451,9 @@ class BasePathNamespace:
|
||||
TDBTOOL = '/usr/bin/tdbtool'
|
||||
SECRETS_TDB = '/var/lib/samba/private/secrets.tdb'
|
||||
LETS_ENCRYPT_LOG = '/var/log/letsencrypt/letsencrypt.log'
|
||||
IPA_CCACHE_SWEEPER_GSSPROXY_SOCK = (
|
||||
"/var/lib/gssproxy/ipa_ccache_sweeper.sock"
|
||||
)
|
||||
|
||||
def check_paths(self):
|
||||
"""Check paths for missing files
|
||||
|
@ -522,6 +522,7 @@ class RedHatTaskNamespace(BaseTaskNamespace):
|
||||
HTTP_CCACHE=paths.HTTP_CCACHE,
|
||||
HTTPD_USER=constants.HTTPD_USER,
|
||||
IPAAPI_USER=ipaapi_user,
|
||||
SWEEPER_SOCKET=paths.IPA_CCACHE_SWEEPER_GSSPROXY_SOCK,
|
||||
)
|
||||
)
|
||||
|
||||
|
@ -12,6 +12,7 @@ import pytest
|
||||
import time
|
||||
from datetime import datetime
|
||||
|
||||
from ipalib.constants import IPAAPI_USER
|
||||
from ipaplatform.paths import paths
|
||||
|
||||
from ipatests.test_integration.base import IntegrationTest
|
||||
@ -205,15 +206,15 @@ class TestPWPolicy(IntegrationTest):
|
||||
"--user-auth-type", "otp"])
|
||||
kinit_check_life(self.master, USER1)
|
||||
|
||||
def test_ccache_sweep(self, reset_to_default_policy):
|
||||
"""Test that the ccache sweeper works
|
||||
def test_ccache_sweep_expired(self, reset_to_default_policy):
|
||||
"""Test that the ccache sweeper works on expired ccaches
|
||||
|
||||
- Force wipe all existing ccaches
|
||||
- Set the ticket policy to a short value, 30 seconds.
|
||||
- Set the ticket policy to a short value, 20 seconds.
|
||||
- Do a series of kinit, ipa command, kdestroy to generate ccaches
|
||||
- sleep()
|
||||
- sleep() for expiration
|
||||
- Run the sweeper
|
||||
- Verify that all ccaches are gone
|
||||
- Verify that all expired ccaches are gone
|
||||
"""
|
||||
MAXLIFE = 20
|
||||
reset_to_default_policy(self.master) # this will reset at END of test
|
||||
@ -226,16 +227,58 @@ class TestPWPolicy(IntegrationTest):
|
||||
['find', paths.IPA_CCACHES, '-type', 'f', '-delete']
|
||||
)
|
||||
for _i in range(5):
|
||||
tasks.kdestroy_all(self.master)
|
||||
tasks.kinit_admin(self.master)
|
||||
self.master.run_command(['ipa', 'user-show', 'admin'])
|
||||
tasks.kdestroy_all(self.master)
|
||||
time.sleep(MAXLIFE)
|
||||
self.master.run_command(
|
||||
['/usr/libexec/ipa/ipa-ccache-sweeper', '-m', '0']
|
||||
tasks.kdestroy_all(self.master)
|
||||
|
||||
result = self.master.run_command(
|
||||
"ls -1 {0} | wc -l".format(paths.IPA_CCACHES)
|
||||
)
|
||||
time.sleep(5)
|
||||
assert int(result.stdout_text.strip()) == 5
|
||||
|
||||
# let ccache expire
|
||||
time.sleep(MAXLIFE)
|
||||
ccache_sweep_cmd = ["/usr/libexec/ipa/ipa-ccache-sweeper", "-m", "0"]
|
||||
|
||||
# should be run as ipaapi for GSSProxy
|
||||
self.master.run_command(
|
||||
["runuser", "-u", IPAAPI_USER, "--"] + ccache_sweep_cmd
|
||||
)
|
||||
|
||||
result = self.master.run_command(
|
||||
"ls -1 {0} | wc -l".format(paths.IPA_CCACHES)
|
||||
)
|
||||
assert int(result.stdout_text.strip()) == 0
|
||||
|
||||
def test_ccache_sweep_valid(self):
|
||||
"""Test that the ccache sweeper doesn't remove valid ccaches
|
||||
- Force wipe all existing ccaches
|
||||
- Run the sweeper
|
||||
- Verify that all valid ccaches weren't removed
|
||||
Note: assumed that ccache expiration doesn't happen during test
|
||||
"""
|
||||
tasks.kdestroy_all(self.master)
|
||||
self.master.run_command(
|
||||
["find", paths.IPA_CCACHES, "-type", "f", "-delete"]
|
||||
)
|
||||
|
||||
for _i in range(5):
|
||||
tasks.kinit_admin(self.master)
|
||||
self.master.run_command(["ipa", "user-show", "admin"])
|
||||
tasks.kdestroy_all(self.master)
|
||||
|
||||
result = self.master.run_command(
|
||||
"ls -1 {0} | wc -l".format(paths.IPA_CCACHES)
|
||||
)
|
||||
assert int(result.stdout_text.strip()) == 5
|
||||
|
||||
ccache_sweep_cmd = ["/usr/libexec/ipa/ipa-ccache-sweeper", "-m", "0"]
|
||||
|
||||
# should be run as ipaapi for GSSProxy
|
||||
self.master.run_command(
|
||||
["runuser", "-u", IPAAPI_USER, "--"] + ccache_sweep_cmd
|
||||
)
|
||||
result = self.master.run_command(
|
||||
"ls -1 {0} | wc -l".format(paths.IPA_CCACHES)
|
||||
)
|
||||
assert int(result.stdout_text.strip()) == 5
|
||||
|
Loading…
Reference in New Issue
Block a user