Restricting API users to certain domains or limiting to one client's resources

Discussion in 'ISPConfig 3 Priority Support' started by kobuki, Oct 30, 2018.

  1. kobuki

    kobuki Member HowtoForge Supporter

    As the subject says - is it possible to restrict API users to certain domains or limiting an API user to a client, possibly assigning an 1:1 rights assignment? I'm asking because I'm using a few domains with Let's Encrypt wildcards and that requires DNS challenge validation. It's working nicely with ISPConfig, but the access restrictions are too wide. An API user granted DNS changing rights can modify any domain, not just their own, and that's very easy to misuse, even by unattended typos in domain names, etc.
     
  2. till

    till Super Moderator Staff Member ISPConfig Developer

    The API runs as admin user, there are no restrictions implemented at the moment.
     
  3. kobuki

    kobuki Member HowtoForge Supporter

    Are there any plans regarding this, or should I look at writing a plugin (for the acme.sh LE client, which is what I'm using mostly) that uses the frontend rest mechanism for this?
     
  4. till

    till Super Moderator Staff Member ISPConfig Developer

    There exists a feature request for this but I can't say when we might be able to implement this as it will require basically to rewrite the API completely.
     
  5. kobuki

    kobuki Member HowtoForge Supporter

  6. till

    till Super Moderator Staff Member ISPConfig Developer

    What we should consider is doing a third route which is less work than rewriting the API and which might become handy when adding support for acme.sh in ispconfig anyway. We could add a new REST api endpoint for acme.sh and have a kind of security token generated for each dns zone which is shown to the user in the ISPConfig GUI and acme.sh can use that security token to authenticate itself against the new api endpoint and this api endpoint is just able to do what acme.sh requires (adding a text record if I remember correctly?).
     
  7. kobuki

    kobuki Member HowtoForge Supporter

    Yes, the acme dns challenge is based on txt record manipulation. 2 operations can be supported: add and remove. Remove is optional. Add adds a new record or overwrites it (optionally doing checks and denying addition in case of a conflict, etc.), and remove removes it, obviously. A challenge in the txt record is used only once, so it's safe to leave it there, thus the remove op. is not required.

    From your idea it looks as if you'd put the new secret code mechanism in the normal rest api of the frontend? What would be de the difference from my proposal besides restricting the dns calls to a single domain to which the secret code belongs? Unless I misunderstood it. I think a user-level restriction is more convenient, even if slightly less secure. Also, acme.sh curently doesn't support per-domain credentials. But if it's anything that doesn't require rewriting the whole auth subsystem in ISPC I'm willing to work on it. Especially if it makes the procerure simpler as coding this in a shell language is... meh.

    EDIT: oh, you probably tohught of the secret code as the only means of authentication in this narrow case where a single txt record needs to be changed, without other forms of auth. Hmm, that indeed is a lot simpler. That way the bulk of the record manipulation can be done on the ISPC backend.
     
  8. till

    till Super Moderator Staff Member ISPConfig Developer

    Yes, the secret code is just for authentication purposes and to make it easier for the users, we use just one token instead of username and password. This token gets auto generated and is stored in the dns_soa table together with the other dns zone data. Or we decide to bind the token to the client so that it is valid for all domains of the client, might make more sense when it comes to ssl certs that contain multiple domains.

    Then we need just two new remote api function (which do not require a session like most other remote api functions) like:

    acmesh_dns_add($secret,$domain,$token);
    acmesh_dns_del($secret,$domain);

    where secret ist the 'secret' that we have either bound to the client or dns record in ispconfig, domain is the domain name and $token is the 'data' portion that acme sets for the txt record.

    I guess that's quite straightforward to implement, add a field for the secret in client or dns_soa table, add some code to fill this field automatically for new and existing clients/zones an finally add that function to the remote api which checks if the token is valid and then adds the required txt record or removes it.
     
  9. kobuki

    kobuki Member HowtoForge Supporter

    Yeah, this looks straightforward. I'd suggest putting the token/secret at the client level, and also suggest making it user-changeable with possibly a button to generate one, like for the password fields. It's very similar to passwords after all and could be compromised in the same way. This has the benefit of being able to use it for multiple domains in a single cert as you noted and when the user does change it, it's only necessary to update in a single place in some client record. If it's on the client level, where should it be? client or sys_user, some other place? I think if we can agree on these details I can implement it in the coming weeks.
     
  10. till

    till Super Moderator Staff Member ISPConfig Developer

    I would put that in the client table. The ability to re-generate is good (maybe have a button for that beside the token field?), but we should consider locking the input field so that it's not editable by the user and generate a long and secure token automatically as I fear users might tend to use something too short/insecure otherwise.
     
  11. kobuki

    kobuki Member HowtoForge Supporter

    OK, I was thinking it over again, but what would be the use of the token and the secret code (see your own sample functions)? Token is for identification of the client in place of the user name, and secret code is in place of the password? In this case only the secret code would need a generate button. Usually APIs for this kind of remoting use a user id/email/random token and a secret code. For ideas: https://github.com/Neilpang/acme.sh/tree/master/dnsapi
     
  12. till

    till Super Moderator Staff Member ISPConfig Developer

    No, the secret is for authentication. The token is the string that Let's encrypt requires as data in the TXT DNS record. So in the client table, we just need one field for the secret code.

    Makes no sense to request unneeded information as a function parameter. We neither need a user id nor an email address. We just need the domain name, the secret and the LE auth token.
     
  13. kobuki

    kobuki Member HowtoForge Supporter

    OK, I'm stupid, the "token" is the txt record challenge string. So we go the domain -> client path for cliend ID. There's a problem though. The domain is not strictly a domain, but rather, a host name, per https://github.com/Neilpang/acme.sh/wiki/DNS-API-Dev-Guide docs, in one of the following forms or similar ones:
    1. _acme-challenge.www.example.com
    2. _acme-challenge.example.com
    3. _acme-challenge.example.co.uk
    4. _acme-challenge.www.example.co.uk
    5. _acme-challenge.sub1.sub2.www.example.co.uk
    So, first we must match the host name to the domain with existing domains as prefixes. If we don't filter on the client's domains first, we need to do queries on the whole set of domains handled by the ISPConfig instance which is not really optimal. We can't filter on the secret as it can theoretically be duplicated across clients. Very small possibility, but exists. On generation it's possible to defend against that, though, but for a secret code, I'm not sure if it would be the proper way to handle it. Any ideas?
     
  14. till

    till Super Moderator Staff Member ISPConfig Developer

    We can assume that the secret is unique as that's a requirement for such an authentication system when you don't use a username and as we auto generate the secret and its a long string like 30 - 60 chars, numbers, special chars etc it is no problem to have it unique.

    In regard to the domain name, is there no way that acme.sh is able to pass the domain name that one passes to the domain commandline option to the api?
     
  15. till

    till Super Moderator Staff Member ISPConfig Developer

    And if acme.sh requires a username, then we can use the client username together with a secret, just one additional parameter in the function but if acme.sh requires it anyway, then we can do it like that.
     
  16. kobuki

    kobuki Member HowtoForge Supporter

    The host name you pass to acme.sh (and to any ACME client in general) is like in my list above, but without the _acme-challenge prefix. It can pertain to the apex (the registered domain name itself) as a special case, and anything else under that domain as subdomain/host name. The actual parameter of the hook is like in that list, including the _acme-challenge part. Certain client hooks dig out the apex in the shell hook script from that so they can address the domain name itself for remote calls (dns.he.net hook is one example). We can do this on the server side more conveniently.

    OTOH, if the secret is not unique, the credentials become unique when they're passed together with the txt record name discussed above. If the secret is required to be unique, then we can look up the client using just that. It's not a requirement for the acme.sh client, it's something we need to consider when implementing the API change. acme.sh passes the challenge string and the host name (_acme-challenge. included) to the configured hook, nothing else. Credentials are passed to the hook via environment vars. It's at the hook's discretion as to what to do with them. We'll need to pass the API URL and the secret code.

    For the existing API-based hook it looks like this:
    Code:
    export ISPC_User="xxx"
    export ISPC_Password="xxx"
    export ISPC_Api="https://ispc.domain.tld:8080/remote/json.php"
    export ISPC_Api_Insecure=1
    My proposal for the new solution:
    Code:
    export ISPC_Secret="xxx"
    export ISPC_Api="https://ispc.domain.tld:8080/"
    I would make https required. What do you think?
     
  17. kobuki

    kobuki Member HowtoForge Supporter

    So, how should I continue? @till?
     
  18. kobuki

    kobuki Member HowtoForge Supporter

    Well, I was looking into the remoting API and found a few things. There are already provisions for client-level auth for remoting, but it looks like unfinished code. If one logins to the remoting API passing a third parameter $client_login set to true, the system checks the user login against the normal sys_user table and applies rights on the user level. However, there's a can_use_api column in the client table which is never written, but checked in the remoting code, failing all user-level logins with a "SOAP Error: The login failed. Client may not use api." message.

    I think that instead of additional hack-like functions and alternative auth secrets, this could be fixed and client-level functions became available for the whole API. It might have not been thoroughly tested so it might have not been enabled yet.
     
  19. till

    till Super Moderator Staff Member ISPConfig Developer

    Sure, we can try to fix that as well but needs definitely a thorough review to not open up a can of worms here. I had not in mind that we started implementing that already.
     
  20. kobuki

    kobuki Member HowtoForge Supporter

    I'm currently looking at the code but I must say I'm not up to the task of thoroughly testing it as I don't know the system that deeply. It would be an improportionally huge task for me. So if someone better fit for the job can't pick up this task we should go on with the previous plan. Of course this is only useful if you decide to include the solution upstream when finished, otherwise I can just build ourselves a simple API gateway/translator that keeps compatibility with the existing acme.sh hook but uses a different URL where the gateway resides (and uses existing user cedentials checking).
     

Share This Page