Pulp-cli 0.25.1 token authentication by --header option

Hello,
Has anyone tried pulp-cli 0.25.1 with token authentication in header? The version has an option --header :

  --header TEXT        Custom header to add to each api call. Name
                       and value are colon separated. Can be
                       specified multiple times.

I tried with pulp -v --header 'Authorization: Token xxxxxxxxxxxxxxxx' rpm repository list , it still asks username and password. Not sure if I am doing the right way?
Or maybe this feature is not officially ready yet? seen an issue here Add support for using Token as an authentication method Ā· Issue #925 Ā· pulp/pulp-cli Ā· GitHub

Thanks!

Hi @xm1234567

I think youā€™re doing it right. Also, you can confirm it by using the -vvv option which should print all calls with headers and bodies to the API.

This is happening because the CLI uses the information it retrieves from the openapi files generated by the API. And we didnā€™t add the proper support to the CLI to ignore it when youā€™re sending the Authorization header by hand.

Any contribution to the issue is welcome.

2 Likes

I tried with pulp-cli 0.25.7 and -vvv, yes, it is Authorization: Basic DWREZWU: , as shown under:

pulp -vvv --header 'Authorization:Token xxxxx --base-url https://x.x.x.x user list
users_list : get https://x.x.x.x/pulp/api/v3/users/?offset=0&limit=25
  User-Agent: Pulp-CLI/0.25.7
  Accept-Encoding: gzip, deflate
  Accept: application/json
  Connection: keep-alive
  Authorization: Basic DWREZWU=
Response: 401
  Server: nginx/1.16.1
  Date: Fri, 21 Jun 2024 14:50:17 GMT
  Content-Type: application/json
  Content-Length: 39
  Connection: keep-alive
  WWW-Authenticate: Token
  Vary: Accept
  Allow: GET, POST, HEAD, OPTIONS
  X-Frame-Options: DENY
  X-Content-Type-Options: nosniff
  Referrer-Policy: same-origin
  Cross-Origin-Opener-Policy: same-origin
  Correlation-ID: 059a84dfa82296c8ee719b409
  Access-Control-Expose-Headers: Correlation-ID
{"detail":"Invalid username/password."}

Sorry, I am not a develop, donā€™t know Django :frowning: Wish someone else can contribute this quicky ?

1 Like

Hey, no problem. We can figure this out together.

Right now, itā€™s just saying that the access was denied, given an invalid username and/or password.

Have you configured your pulp instance to accept a header as an authentication method?
Can you check the AUTHENTICATION_BACKENDS and the DEFAULT_AUTHENTICATION_CLASSES under REST_FRAMEWORK variables in your settings?

Itā€™s probably something related to the configuration of your instance.

1 Like

Hello @decko , I input fake username and password in my test when it asked. The token is the correct, but since it didnā€™t take in account the token, so then it asked username and password, then I input fake username and password. which lead to Invalid username/password .

In my pulp instance:

AUTHENTICATION_BACKENDS = [
    'social_core.backends.keycloak.KeycloakOAuth2',# Accept Keycloak auth
    'dynaconf_merge',
]

and I use this :

REST_FRAMEWORK__DEFAULT_AUTHENTICATION_CLASSES = ['rest_framework.authentication.TokenAuthentication','dynaconf_merge']

Here is the whole settings.py file:

Summary

SECRET_KEY = ā€œxxxā€
CONTENT_ORIGIN = ā€œhttp://pulp_content:24816ā€
DATABASES = {ā€œdefaultā€: {ā€œHOSTā€: ā€œpostgresā€, ā€œENGINEā€: ā€œdjango.db.backends.postgresqlā€, ā€œNAMEā€: ā€œpulpā€, ā€œUSERā€: ā€œpulpā€, ā€œPASSWORDā€: ā€œxxxā€, ā€œPORTā€: ā€œ5432ā€, ā€œCONN_MAX_AGEā€: 0, ā€œOPTIONSā€: {ā€œsslmodeā€: ā€œpreferā€}}}
CACHE_ENABLED = True
REDIS_HOST = ā€œredisā€
REDIS_PORT = 6379
REDIS_PASSWORD = ā€œā€
ANSIBLE_API_HOSTNAME = ā€œhttp://pulp_api:24817ā€
ANSIBLE_CONTENT_HOSTNAME = ā€œhttp://pulp_content:24816/pulp/contentā€
ALLOWED_IMPORT_PATHS = ["/tmp"]
ALLOWED_EXPORT_PATHS = ["/tmp"]
TOKEN_SERVER = ā€œhttp://pulp_api:24817/token/ā€
TOKEN_AUTH_DISABLED = False
TOKEN_SIGNATURE_ALGORITHM = ā€œES256ā€
PUBLIC_KEY_PATH = ā€œ/etc/pulp/keys/container_auth_public_key.pemā€
PRIVATE_KEY_PATH = ā€œ/etc/pulp/keys/container_auth_private_key.pemā€
ANALYTICS = False
STATIC_ROOT = ā€œ/var/lib/operator/static/ā€

CSRF_TRUSTED_ORIGINS = [ā€˜https://x.x.xā€™,]
SOCIAL_AUTH_REDIRECT_IS_HTTPS = True

#Add sha1
ALLOWED_CONTENT_CHECKSUMS = [ā€œsha224ā€, ā€œsha256ā€, ā€œsha384ā€, ā€œsha512ā€, ā€œsha1ā€]

#For token creation
REST_FRAMEWORK__DEFAULT_AUTHENTICATION_CLASSES = [ā€˜rest_framework.authentication.TokenAuthenticationā€™,ā€˜dynaconf_mergeā€™]

#For keycloak and token creation
INSTALLED_APPS = [
ā€˜social_djangoā€™, # ā† for keyloak
ā€˜rest_framework.authtokenā€™,# ā† here is for token creation
ā€˜dynaconf_mergeā€™, # for dynaconf merge token
]

#For keycloak
AUTHENTICATION_BACKENDS = [
ā€˜social_core.backends.keycloak.KeycloakOAuth2ā€™,# Accept Keycloak auth
ā€˜dynaconf_mergeā€™,
]
#For keycloak
TEMPLATES = [{ā€˜APP_DIRSā€™: True,
ā€˜BACKENDā€™: ā€˜django.template.backends.django.DjangoTemplatesā€™,
ā€˜DIRSā€™: [ā€™/usr/local/lib/python3.8/site-packages/pulpcore/app/templatesā€™],
ā€˜OPTIONSā€™: {ā€˜context_processorsā€™: [ā€˜django.template.context_processors.debugā€™,
ā€˜django.template.context_processors.requestā€™,
ā€˜django.contrib.auth.context_processors.authā€™,
ā€˜social_django.context_processors.backendsā€™,
ā€˜social_django.context_processors.login_redirectā€™,
ā€˜django.contrib.messages.context_processors.messagesā€™]}}]

1 Like

Is there version requirement for the --header option? On my pulp instance, the pulp core and pump_rpm version are:

{
    "versions": [
        {
            "component": "core",
            "version": "3.45.1",
            "package": "pulpcore",
            "module": "pulpcore.app",
            "domain_compatible": true
        },
...
        {
            "component": "rpm",
            "version": "3.25.0",
            "package": "pulp-rpm",
            "module": "pulp_rpm.app",
            "domain_compatible": true
        },
....

The detailed pulp instance status info:

Summary

{
ā€œversionsā€: [
{
ā€œcomponentā€: ā€œcoreā€,
ā€œversionā€: ā€œ3.45.1ā€,
ā€œpackageā€: ā€œpulpcoreā€,
ā€œmoduleā€: ā€œpulpcore.appā€,
ā€œdomain_compatibleā€: true
},
{
ā€œcomponentā€: ā€œansibleā€,
ā€œversionā€: ā€œ0.21.1ā€,
ā€œpackageā€: ā€œpulp-ansibleā€,
ā€œmoduleā€: ā€œpulp_ansible.appā€,
ā€œdomain_compatibleā€: false
},
{
ā€œcomponentā€: ā€œcontainerā€,
ā€œversionā€: ā€œ2.18.0ā€,
ā€œpackageā€: ā€œpulp-containerā€,
ā€œmoduleā€: ā€œpulp_container.appā€,
ā€œdomain_compatibleā€: false
},
{
ā€œcomponentā€: ā€œdebā€,
ā€œversionā€: ā€œ3.1.1ā€,
ā€œpackageā€: ā€œpulp_debā€,
ā€œmoduleā€: ā€œpulp_deb.appā€,
ā€œdomain_compatibleā€: false
},
{
ā€œcomponentā€: ā€œmavenā€,
ā€œversionā€: ā€œ0.8.0ā€,
ā€œpackageā€: ā€œpulp-mavenā€,
ā€œmoduleā€: ā€œpulp_maven.appā€,
ā€œdomain_compatibleā€: false
},
{
ā€œcomponentā€: ā€œostreeā€,
ā€œversionā€: ā€œ2.2.1ā€,
ā€œpackageā€: ā€œpulp-ostreeā€,
ā€œmoduleā€: ā€œpulp_ostree.appā€,
ā€œdomain_compatibleā€: false
},
{
ā€œcomponentā€: ā€œpythonā€,
ā€œversionā€: ā€œ3.11.0ā€,
ā€œpackageā€: ā€œpulp-pythonā€,
ā€œmoduleā€: ā€œpulp_python.appā€,
ā€œdomain_compatibleā€: false
},
{
ā€œcomponentā€: ā€œrpmā€,
ā€œversionā€: ā€œ3.25.0ā€,
ā€œpackageā€: ā€œpulp-rpmā€,
ā€œmoduleā€: ā€œpulp_rpm.appā€,
ā€œdomain_compatibleā€: true
},
{
ā€œcomponentā€: ā€œcertguardā€,
ā€œversionā€: ā€œ3.45.1ā€,he

Ok. I believe there are a bunch of fixes and changes on the main branch of pulp-cli, even one that corrects the behavior of asking for username/password when using --header.

Can you try it and return here the outcome?

1 Like

I donā€™t see any.
But hey you asked: Prevent Authorization header to be overwritten by mdellweg Ā· Pull Request #1002 Ā· pulp/pulp-cli Ā· GitHub

I must tell you however this is a bad workaround and it will give the worst user experience for use with short lived bearer tokens. It might be ok for never expiring (only actively being revoked) deployment api tokens.

@xm1234567 Can you confirm that this fixes the issue?

Hello, sorry for the late reply. I still have the same problem, it still asked username and password when I use --header option. My test environment is:

(venv) # pip list | grep pulp
pulp-cli           0.25.7
pulp-glue          0.25.7

            "component": "core",
            "version": "3.52.0",
            "package": "pulpcore",
            "module": "pulpcore.app",
            "domain_compatible": true

            "component": "rpm",
            "version": "3.25.3",
            "package": "pulp-rpm",
            "module": "pulp_rpm.app",
            "domain_compatible": true

@decko I donā€™t know if 0.25.7 is what you mean the the main branch ?

Here is the output of the test:

(venv) [root@xxxx ~]# pulp -vvv --header 'Authorization:Token xxxxx' user list
Username: tt
Password: 
users_list : get https://xx.xx.xx/pulp/api/v3/users/?offset=0&limit=25
  User-Agent: Pulp-CLI/0.25.7
  Accept-Encoding: gzip, deflate
  Accept: application/json
  Connection: keep-alive
  Authorization: Basic dHQ6dHQ=
Response: 401
  Server: nginx/1.16.1
  Date: Wed, 26 Jun 2024 19:51:30 GMT
  Content-Type: application/json
  Content-Length: 39
  Connection: keep-alive
  WWW-Authenticate: Token
  Vary: Accept
  Allow: GET, POST, HEAD, OPTIONS
  X-Frame-Options: DENY
  X-Content-Type-Options: nosniff
  Referrer-Policy: same-origin
  Cross-Origin-Opener-Policy: same-origin
  Correlation-ID: 2854b41c45914256bc1f2632d164ffbf
  Access-Control-Expose-Headers: Correlation-ID
{"detail":"Invalid username/password."}
Error: {"detail":"Invalid username/password."}


(venv) [root@xxx ~]# pulp -vvv --header 'Authorization: Token xxxxxx' user list
Username: tt
Password: 
Traceback (most recent call last):
  File "/root/venv/bin/pulp", line 8, in <module>
    sys.exit(main())
  File "/root/venv/lib64/python3.9/site-packages/click/core.py", line 1157, in __call__
    return self.main(*args, **kwargs)
  File "/root/venv/lib64/python3.9/site-packages/click/core.py", line 1078, in main
    rv = self.invoke(ctx)
  File "/root/venv/lib64/python3.9/site-packages/pulpcore/cli/common/generic.py", line 290, in invoke
    return super().invoke(ctx)
  File "/root/venv/lib64/python3.9/site-packages/click/core.py", line 1688, in invoke
    return _process_result(sub_ctx.command.invoke(sub_ctx))
  File "/root/venv/lib64/python3.9/site-packages/pulpcore/cli/common/generic.py", line 290, in invoke
    return super().invoke(ctx)
  File "/root/venv/lib64/python3.9/site-packages/click/core.py", line 1688, in invoke
    return _process_result(sub_ctx.command.invoke(sub_ctx))
  File "/root/venv/lib64/python3.9/site-packages/pulpcore/cli/common/generic.py", line 290, in invoke
    return super().invoke(ctx)
  File "/root/venv/lib64/python3.9/site-packages/click/core.py", line 1434, in invoke
    return ctx.invoke(self.callback, **ctx.params)
  File "/root/venv/lib64/python3.9/site-packages/click/core.py", line 783, in invoke
    return __callback(*args, **kwargs)
  File "/root/venv/lib64/python3.9/site-packages/click/decorators.py", line 92, in new_func
    return ctx.invoke(f, obj, *args, **kwargs)
  File "/root/venv/lib64/python3.9/site-packages/click/core.py", line 783, in invoke
    return __callback(*args, **kwargs)
  File "/root/venv/lib64/python3.9/site-packages/click/decorators.py", line 92, in new_func
    return ctx.invoke(f, obj, *args, **kwargs)
  File "/root/venv/lib64/python3.9/site-packages/click/core.py", line 783, in invoke
    return __callback(*args, **kwargs)
  File "/root/venv/lib64/python3.9/site-packages/pulpcore/cli/common/generic.py", line 1252, in callback
    result = entity_ctx.list(limit=limit, offset=offset, parameters=kwargs)
  File "/root/venv/lib64/python3.9/site-packages/pulp_glue/common/context.py", line 767, in list
    result: t.Mapping[str, t.Any] = self.call("list", parameters=payload)
  File "/root/venv/lib64/python3.9/site-packages/pulp_glue/common/context.py", line 704, in call
    return self.pulp_ctx.call(
  File "/root/venv/lib64/python3.9/site-packages/pulp_glue/common/context.py", line 357, in call
    result = self.api.call(
  File "/root/venv/lib64/python3.9/site-packages/pulp_glue/common/openapi.py", line 701, in call
    request: requests.PreparedRequest = self.render_request(
  File "/root/venv/lib64/python3.9/site-packages/pulp_glue/common/openapi.py", line 612, in render_request
    request = self._session.prepare_request(
  File "/root/venv/lib64/python3.9/site-packages/requests/sessions.py", line 486, in prepare_request
    p.prepare(
  File "/root/venv/lib64/python3.9/site-packages/requests/models.py", line 369, in prepare
    self.prepare_headers(headers)
  File "/root/venv/lib64/python3.9/site-packages/requests/models.py", line 491, in prepare_headers
    check_header_validity(header)
  File "/root/venv/lib64/python3.9/site-packages/requests/utils.py", line 1040, in check_header_validity
    _validate_header_part(header, value, 1)
  File "/root/venv/lib64/python3.9/site-packages/requests/utils.py", line 1056, in _validate_header_part
    raise InvalidHeader(
requests.exceptions.InvalidHeader: Invalid leading whitespace, reserved character(s), or returncharacter(s) in header value: ' Token xxxxxxxxxxxxxxx'

Strange, same command , I did it several times, and once it says {"detail":"Invalid username/password."}, Another time, it says requests.exceptions.InvalidHeader: Invalid leading whitespace, reserved character(s), or returncharacter(s) in header value: ' Token xxxxxxx'

I tested with pulp-cli 0.25.7 which is installed by pip and probably is not the same as main branch on github.
I donā€™t know how to test with the branch. @decko if you can explain to me how?
Also can you check my settings.py is configured correctly to accept a header as an authentication method?

Hi @xm1234567

Sorry for the late response.
I just checked on PiPY and seems that we got the latest 0.26 version there. Could you try to update the CLI and try again?

Also, from where you took the instructions to use Pulp with Keycloak?
Iā€™ve never tested this combination, but looking to your config Iā€™m seeing no major issues.

Could you just try to remove the dynaconf_merge instruction from AUTHENTICATION_BACKENDS and DEFAULT_AUTHENTICATION_CLASSES and see what happens?