saltstack / salt

Software to automate the management and configuration of any infrastructure or application at scale. Install Salt from the Salt package repositories here:
https://docs.saltproject.io/salt/install-guide/en/latest/
Apache License 2.0
14.21k stars 5.48k forks source link

Saltstack not working without admin rights #55161

Open gigi206 opened 5 years ago

gigi206 commented 5 years ago

Description of Issue

When I run saltstack with admin rights it works.

But if I run saltstack without admin rights, I have the following issue:

{%- from 'salt/utils/init.jinja' import init with context %}    <======================
{%- from 'salt/core/git/map.jinja' import git with context %}

{{ init(git, action='install') }}
Traceback (most recent call last):
  File "C:\VSCode-Anywhere\Apps\saltstack\bin\lib\site-packages\salt\utils\templates.py", line 394, in render_jinja_tmpl
[...]
---
[CRITICAL] Rendering SLS 'salt:salt/core/git/install' failed: Jinja error: (1314, 'GetNamedSecurityInfo', 'Le client ne dispose pas d\u2019un privilège nécessaire.')
Traceback (most recent call last):
  File "C:\VSCode-Anywhere\Apps\saltstack\bin\lib\site-packages\salt\utils\templates.py", line 394, in render_jinja_tmpl
    output = template.render(**decoded_context)
  File "C:\VSCode-Anywhere\Apps\saltstack\bin\lib\site-packages\jinja2\environment.py", line 1008, in render
    return self.environment.handle_exception(exc_info, True)
  File "C:\VSCode-Anywhere\Apps\saltstack\bin\lib\site-packages\jinja2\environment.py", line 780, in handle_exception
    reraise(exc_type, exc_value, tb)
  File "C:\VSCode-Anywhere\Apps\saltstack\bin\lib\site-packages\jinja2\_compat.py", line 37, in reraise
    raise value.with_traceback(tb)
  File "<template>", line 10, in top-level template code
  File "C:\VSCode-Anywhere\Apps\saltstack\bin\lib\site-packages\salt\utils\jinja.py", line 158, in get_source
    self.check_cache(_template)
  File "C:\VSCode-Anywhere\Apps\saltstack\bin\lib\site-packages\salt\utils\jinja.py", line 119, in check_cache
    self.cache_file(template)
  File "C:\VSCode-Anywhere\Apps\saltstack\bin\lib\site-packages\salt\utils\jinja.py", line 112, in cache_file
    self.file_client().get_file(saltpath, '', True, self.saltenv)
  File "C:\VSCode-Anywhere\Apps\saltstack\bin\lib\site-packages\salt\fileclient.py", line 1227, in get_file
    fn_.close()
  File "C:\VSCode-Anywhere\Apps\saltstack\bin\lib\site-packages\salt\utils\atomicfile.py", line 127, in close
    source=self._filename, target=self._tmp_filename)
  File "C:\VSCode-Anywhere\Apps\saltstack\bin\lib\site-packages\salt\utils\win_dacl.py", line 2013, in copy_security
    source, obj_type_flag, security_flags)
pywintypes.error: (1314, 'GetNamedSecurityInfo', 'Le client ne dispose pas d\u2019un privilège nécessaire.')

GetNamedSecurityInfo needs to be run with admin rights

Steps to Reproduce Issue

I think just import from a jinja file like that :

{%- from 'xxx/xxx.jinja' import xxx with context %}

Versions Report

Salt Version:
           Salt: 2019.2.2

Dependency Versions:
           cffi: 1.12.2
       cherrypy: 17.4.1
       dateutil: 2.8.0
      docker-py: Not Installed
          gitdb: 2.0.6
      gitpython: 2.1.10
          ioflo: Not Installed
         Jinja2: 2.10.1
        libgit2: Not Installed
        libnacl: 1.6.1
       M2Crypto: Not Installed
           Mako: 1.0.7
   msgpack-pure: Not Installed
 msgpack-python: 0.5.6
   mysql-python: Not Installed
      pycparser: 2.19
       pycrypto: Not Installed
   pycryptodome: 3.8.1
         pygit2: Not Installed
         Python: 3.5.4 (v3.5.4:3f56838, Aug  8 2017, 02:17:05) [MSC v.1900 64 bit (AMD64)]
   python-gnupg: 0.4.4
         PyYAML: 3.13
          PyZMQ: 18.0.1
           RAET: Not Installed
          smmap: 2.0.5
        timelib: 0.2.4
        Tornado: 4.5.3
            ZMQ: 4.3.1

System Versions:
           dist:
         locale: cp1252
        machine: AMD64
        release: 10
         system: Windows
        version: 10 10.0.18362 SP0 Multiprocessor Free
frogunder commented 5 years ago

@gigi206 Thank you for reporting this issue.

@saltstack/team-windows Any of you have any thoughts on this one? Thanks.

arizvisa commented 5 years ago

@gigi206, can you make sure that the user running start has write access to the directory for the minion_cache?

To ensure the user can write to the minion cache, the section "Add the New User to the Access Control List for the Salt Folder" at https://docs.saltstack.com/en/latest/topics/installation/windows.html should help, just make sure you're applying it to the minion cache directory.

gigi206 commented 5 years ago

Yes, all rights are good for the minion_cache.

If I edit function copy_security in salt\utils\win_dacl.py file like below, it seems to works :

def copy_security(source,
                  target,
                  obj_type='file',
                  copy_owner=True,
                  copy_group=True,
                  copy_dacl=True,
                  copy_sacl=True):
    return True

As shown in the error message, GetNamedSecurityInfo required an admin privilege.

arizvisa commented 5 years ago

maintainers: it seems like according to https://docs.microsoft.com/en-us/windows/win32/api/aclapi/nf-aclapi-getnamedsecurityinfoa, the SE_SECURITY_NAME privilege needs to be added to the token that's adjusted in salt.platform.win. You can search for AdjustTokenPrivileges to find it and then add the priv to the loop above it.

stale[bot] commented 4 years ago

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

If this issue is closed prematurely, please leave a comment and we will gladly reopen the issue.

arizvisa commented 4 years ago

The fix for this issue was discussed and resolved and is literally just waiting for a maintainer to pick it up and add the required SE_SECURITY_NAME privilege.

stale[bot] commented 4 years ago

Thank you for updating this issue. It is no longer marked as stale.

arizvisa commented 4 years ago

@frugunder, if you want to fix bugs please review the super simple fix mentioned in this discussion.

stale[bot] commented 4 years ago

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

If this issue is closed prematurely, please leave a comment and we will gladly reopen the issue.

gigi206 commented 4 years ago

Not stale

stale[bot] commented 4 years ago

Thank you for updating this issue. It is no longer marked as stale.

arizvisa commented 4 years ago

Not only is it not stale, but a really simple fix too.

twangboy commented 4 years ago

The above suggested fix is incorrect. You can't enable privileges the user does not have.

The token contains all the privileges the user has. Some are enabled, others are dormant. The elevate_token function in salt.platform.win enables all privileges the user has. This is the equivalent of the UAC dialog box when you're doing something from an account that is a member of the Administrators group.

By default, not all privileges are enabled (unless you're logged in as THE Administrator). That function loops through each privilege held by the token and enables it. If the user doesn't have the privilege to begin with, it cannot be enabled.

You must run salt with a user that has permissions to its directories. By default this is Administrators. If you're an administrator, you must run salt in an elevated prompt.

twangboy commented 4 years ago

The failure in the stack trace of the original issue is in the copy_security function in salt.utils.win_dacl. Just above the failure there are the following lines:

    new_privs = set()
    luid = win32security.LookupPrivilegeValue('', 'SeTakeOwnershipPrivilege')
    new_privs.add((luid, win32con.SE_PRIVILEGE_ENABLED))
    luid = win32security.LookupPrivilegeValue('', 'SeRestorePrivilege')
    new_privs.add((luid, win32con.SE_PRIVILEGE_ENABLED))
    luid = win32security.LookupPrivilegeValue('', 'SeSecurityPrivilege')
    new_privs.add((luid, win32con.SE_PRIVILEGE_ENABLED))

    # Get the current token
    p_handle = win32api.GetCurrentProcess()
    t_handle = win32security.OpenProcessToken(
        p_handle,
        win32security.TOKEN_ALL_ACCESS | win32con.TOKEN_ADJUST_PRIVILEGES)

You could try adding the following 2 lines after the las new_privs.add to see if it will add a new permission. I'm not sure it will work:

    luid = win32security.LookupPrivilegeValue('', 'SeSecurityName')
    new_privs.add((luid, win32con.SE_PRIVILEGE_ENABLED))

If this works, could you please submit a PR?

arizvisa commented 4 years ago

The above suggested fix is incorrect. You can't enable privileges the user does not have.

The token contains all the privileges the user has. Some are enabled, others are dormant. The elevate_token function in salt.platform.win enables all privileges the user has. This is the equivalent of the UAC dialog box when you're doing something from an account that is a member of the Administrators group.

I think you're looking at the wrong thing here because salt.platform.win.elevate_token is executed only by salt.utils.win_runas and is related to that particular commit. So, the process' token is not getting all of its available privileges set with elevate_token prior to running GetNamedSecurityInfo.

If elevate_token was actually being called at process startup, then that would make sense...but that's definitely not in OPs case. So specifically these privileges that the OP enabled for their service account are still "dormant". You can literally set a breakpoint at kernelbase!AdjustTokenPrivileges and watch it not get hit when running salt until you explicitly try and call a function that explicitly sets privs.

This is why many of the implementors of the functionality in salt.utils.win_dacl are explicitly opening the process token and explicitly assigning the necessary privileges prior to making the API calls. If elevate_token was guaranteed to have been called for the process token, then the explicit setting of tokens is entirely unnecessary.

And no, the UAC dialog is not the same and isn't even considered a security boundary for that matter as explicitly mentioned by Microsoft (they don't offer security bounties for UAC bypasses).

By default, not all privileges are enabled (unless you're logged in as THE Administrator). That function loops through each privilege held by the token and enables it. If the user doesn't have the privilege to begin with, it cannot be enabled.

You must run salt with a user that has permissions to its directories. By default this is Administrators. If you're an administrator, you must run salt in an elevated prompt.

According to the OP, the discussion from 4 months ago, and my reproduction from 4 months ago, this was the case. I don't recall if it was the same crash since it was so long ago and I just relocated and don't have any of my equipment setup, but you can simply grep for calls to GetNamedSecurityInfo and find a handful of code paths that don't set the token privileges prior to making the call.

The right thing to do would be to set all available token privileges at startup (instead of on-demand) so that one can cull out all of the repeated code, or better yet to mention in docs at https://docs.saltstack.com/en/master/ref/configuration/nonroot.html that running the service as a non-administrative in Windows is although possible, it's unsupported until someone has actually tested it.

As you guys have to unfortunately go through a struggle fixing critical issues upon each release, I think you guys should be culling features and I'm for fixing this w/ docs, but its up to @gigi206 on how they want to proceed.

(edited to remove a stray 'Y')

stale[bot] commented 4 years ago

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

If this issue is closed prematurely, please leave a comment and we will gladly reopen the issue.

gigi206 commented 4 years ago

Not stale

stale[bot] commented 4 years ago

Thank you for updating this issue. It is no longer marked as stale.