Allowing clients to self-register

115 views
Skip to first unread message

bruienne

unread,
May 8, 2013, 8:47:44 PM5/8/13
to munkise...@googlegroups.com
Howdy all,

I made some changes to application_controler.rb to allow new computers to register themselves during the munkitools/Munkiserver installation phase, they can be found here: http://bit.ly/10jrkpK

Right now I have the following one-liner in a postflight which is working fine:

curl ${MUNKISERVER}/${UNIT}/computers -F "computer[name]=${MYHOSTNAME}" -F "computer[hostname]=${MYHOSTNAME}" -F "computer[mac_address]=${MAC}" -F "computer[environment_id]=1" -F "computer[computer_model_id]=16" -F "commit=Add" -F "autoconfig=true"

Basically I'm wrapping both require_login and authorize_resource in an unless to check for an additional parameter that is sent by the client during install postflight. If present, login and authorization are not required and the computer is added.

Some obvious caveats that I can see right now is that this doesn't restrict the authorization exemption to just adding new computers but to any operation that sends the appropriate parameter. I tried to just apply it to the Computer.create and Computer.new methods in computers_controller.rb but I ran into errors with missing definitions of "params".

Of course it would also be a lot more secure to put the whole thing behind SSL and using client certificates for access to certain functionality, like the one I am after. Or, as another alternative, having some kind of robust RESTful API, but that would be a lot more work to plan and implement.

TL;DR: looking for some feedback on my hacky attempt at adding a feature, hoping to improve it, maybe do a pull request.

Thanks,
Pepijn.

Ronald Carter

unread,
Mar 9, 2014, 6:13:23 AM3/9/14
to munkise...@googlegroups.com
I am new to this as in Apple coming from a solely windows envitoment.

We are currently using a previous setup of MunkiServer and I hate having to add each client individualy as such I was wondering if you could

1. Provide information on how to achieve this in conjunction with deploysrudio.

What I am after is having the new MacBook Air auto enrolled in MunkiServer as part of a deployment studio run time?

Jordan Raine

unread,
Mar 10, 2014, 7:54:42 PM3/10/14
to Ronald Carter, munkise...@googlegroups.com
Hey Ronald,
There has been talk of adding a JSON API to add computers but it never materialized. At SFU, we use a simple script that calls rails runner to create a computer record. Is anyone else doing some sort of automatic computer adding?

Jordan
--
You received this message because you are subscribed to the Google Groups "munkiserver-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to munkiserver-d...@googlegroups.com.
To post to this group, send email to munkise...@googlegroups.com.
Visit this group at http://groups.google.com/group/munkiserver-dev.
For more options, visit https://groups.google.com/d/optout.

Ronald Carter

unread,
Mar 10, 2014, 8:00:59 PM3/10/14
to munkise...@googlegroups.com

No one else is doing automatic computer adding as far as I can tell.

Can I please get your script and an explanation on how to use it? Preferably through deploy studio.

You received this message because you are subscribed to a topic in the Google Groups "munkiserver-dev" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/munkiserver-dev/7hbBjtOPJxs/unsubscribe.
To unsubscribe from this group and all its topics, send an email to munkiserver-d...@googlegroups.com.

Pepijn Bruienne

unread,
Mar 10, 2014, 8:21:24 PM3/10/14
to munkise...@googlegroups.com, munkise...@googlegroups.com
Ronald, I replied to you off-list. The modification I made to make auto enrollment work is somewhere in my code archive, I'll pull it up and share what I have. As I said in the email, it's been a while since I actively used it but it sounds like it'll work for you as my install method was DeployStudio as well when I used it.

Thanks,
Pepijn.

---
Pepijn Bruienne

Ricky Chilcott

unread,
Mar 11, 2014, 3:31:58 PM3/11/14
to munkise...@googlegroups.com
We haven’t been doing this at Ohio University yet, but I very much see the need.  I was thinking about building a specific endpoint for it, would anyone have an objection to that?  It would be similar to the checkin endpoint, but would likely be something like  /computers/register?name=blah&mac_address=12%3A34%3A56%3A78%3A90&unit=4&environment=2&computer_group=15

This endpoint would only create a computer if it doesn’t exist. If it does, it would raise an error, if any of the pieces were malformed or un-locatatable (i.e. bad unit id, environment id, or computer_group id) it would raise an error.

This endpoint would not require authentication, though we could add the concept of an API key which would be tied to a user account and check to make sure that user account has permissions in a given unit.  Obviously, this last one is a little more complex and would slow the implementation time.

I could see a DeployStudio script run a curl command with the appropriate attributes pre_populated with environment variables from the DeployStudio groups, something like:

curl "https://munkiserver/computers/register” --data “name=#{DS_COMPUTERNAME}&hostname=${DS_HOSTNAME}&mac_address=#{DS_PRIMARY_MAC_ADDRESS}&unit=${MS_UNIT_ID}&environment=${MS_ENVIRONMENT_ID}&computer_group=${MS_COMPUTER_GROUP_ID}"

Do people have any thoughts about that sort of implementation?

Ricky Chilcott


Pepijn Bruienne

unread,
Mar 11, 2014, 3:43:27 PM3/11/14
to munkise...@googlegroups.com
Ricky, that's pretty much how I solved it, I bypassed auth requirements if a certain form field was passed in. I put up an example of it for my PSU Mac talk last year: http://bit.ly/munkiserver - basically it sends a regular form via POST requesting a new Computer but it bypasses authentication if the additional "autoconfig" parameter is encountered in the request. Having something like an API key in play would be much more secure but that's how I solved it. Be glad to help test and mock up something like it.

Thanks,
Pepijn.
-- 
Pepijn Bruienne
Sent with Airmail

Ricky Chilcott

unread,
Mar 12, 2014, 8:22:15 AM3/12/14
to munkise...@googlegroups.com
I'll have some time tomorrow afternoon to dig into this.  What's the consensus.  I looked up the curl syntax, and I had some stuff wrong from below, but it was enough to illustrate the concept.

Should we namespace API, under something like /api/computers/register?

Also, since there are no tools built into the OS that deal with JSON, we will need support multiple formats.  If we build an API long-term, we'll need to support JSON and either plists (which is going to be a little challenging) or a params based API like I outlined below.  Any thoughts on that?

Ricky Chilcott


Jordan Raine

unread,
Mar 12, 2014, 11:48:11 AM3/12/14
to munkise...@googlegroups.com, munkise...@googlegroups.com
Seems like a great idea to me. I think JSON is definitely the way to go with for the response, while a normal post with parameters would be just fine. Something like: curl --data "foo=bar&baz=qux" http://munki/api/computer/register

The most complex part is definitely securing this URL. I like the idea of an API key, though that may require a UI to generate keys. Would an allowed IP list perhaps do the trick? For the identified use case, the registration would occur from the DeployStudio server only.

Jordan

Sent from my iPhone

Ricky Chilcott

unread,
Mar 12, 2014, 5:37:40 PM3/12/14
to munkise...@googlegroups.com
Should API keys just be tied to users? We could add an api_key attribute to each user, which gets auto generated per user.  We can use that user’s permissions to determine whether actions can be taken.  As a 2nd factor, should we also require a user to be specified too?  I’d lean toward using HTTP Headers for these values, like X-API-USER=rickychilcott and X-API-KEY=abcd1234.

Ricky Chilcott


Pepijn Bruienne

unread,
Mar 12, 2014, 7:26:22 PM3/12/14
to Jordan Raine, munkise...@googlegroups.com, munkise...@googlegroups.com
An additional vote for JSON as well, it makes the most sense from an interoperability standpoint -- Ruby, Python can both easily handle it. As for securing, doing an IP-based ACL would have to include all IP ranges possible a new Computer could register from -- the enroll request would come from the client running DS Runtime, not the DS Server. There's various ways to do an API key with Rails but I think that would have my preference. You'd add the key to your DS (or other deployment tool) first boot scripts and be off and running. I'm thinking there might be different API keys depending on the type of access; computer enrollment vs. Package/Unit/etc. operations.

Thanks,
Pepijn.

Ricky Chilcott

unread,
Mar 17, 2014, 10:16:17 PM3/17/14
to munkise...@googlegroups.com
I didn't get a chance last week to work on this, but this week seems a little more promising.

I'd like to bake API keys in from the outset, so that we know things are secure from the beginning -- particularly because Ohio University is in the planning stages of rolling munkiserver into a better production environment in our data center which will necessitate even higher levels of security.

My proposal would be to add API keys to each user and follow each user’s context for permissioning.  To make things a little more secure, I’d add an API Key and API secret.  The key is equivalent to the username and secret is equivalent to a password.  Both should be kept private, but the secret is more sensitive.  Each user would only have one API key/secret set and can revoke old secrets and generate new.  Anything a user can do manually would be permissible to their API key/secret set.  

I would also advise against IP-based ACLs built in to the app.  The host firewall can take care of firewall rules (and are highly encouraged), but the app would allow anyone with the right API keys to access the service.

Does anyone take any issue with this?

Ricky Chilcott


Jordan Raine

unread,
Mar 18, 2014, 1:14:31 AM3/18/14
to munkise...@googlegroups.com, munkise...@googlegroups.com
Sounds good Ricky. Riley and I talked a bit about this on Friday and thought a Token model (or some name similar) would be best -- it would belong to the user but not be so intricately tied as a column on the user table. I agree about IP-based ACLs. Sounds like it really doesn't fit for this case. 

Ricky, what about having an API key and a secret would be worth the overhead of managing two ransom strings vs one? I think I understand where you're coming regarding security but it seems that handling a single token as very sensitive would be sufficient. 

What are your thoughts?

Sent from my iPhone

Ricky Chilcott

unread,
Mar 18, 2014, 2:51:14 PM3/18/14
to munkise...@googlegroups.com
I guess it could just be a single token.  I just see most services having two factors.  If people are using SSL and we’re tucking the token in a HTTP header, then we should be sufficiently covered from a security perspective.

What would be the reason to keep it out of the user’s table?  If we don’t store it there, where do we store it?  Also, would there be a requirement to have multiple tokens per user?  If so, we’ll want a way to notate what each key is used for. I could see the way to separate the tokens so that if one token is compromised, you can deprecate it while you keep other pieces of infrastructure using other tokens.

Ricky Chilcott


Jordan Raine

unread,
Mar 18, 2014, 3:14:26 PM3/18/14
to Ricky Chilcott, munkise...@googlegroups.com
Keeping it out of the user model keeps it more flexible and allows us to add features as you’ve mentioned. I’m imaging something along the lines of this:

class User < ActiveRecord::Base
  has_many :tokens
  ### more implementation ###
end

class Token < ActiveRecord::Base
  belongs_to :user
  ### more implementation ###
end

user = User.first
user.tokens # => []
user.generate_token # => creates new random token and adds it as an associated token
user.tokens # => returns array of associated tokens, in this case the one we just generated
token = user.tokens.first
token.destroy # => when we have a compromised token or want to rotate to a new one, we can destroy the old one
# Authenticating a user would look something like this
token = Token.find_by_value(“xxxxxxxx”)
current_user = token.user

Jordan

Ricky Chilcott

unread,
Mar 18, 2014, 7:14:08 PM3/18/14
to Jordan Raine, munkise...@googlegroups.com
Makes sense.  I’m cool with doing that.

Does a given token always have the same rights as the owning user?

Per token, I’d say we’ed need the following attributes:

id, integer
user_id, integer
key, String
notes, Text
active, Boolean
created_at, Datetime
created_at, Datetime

Anything else?

Ricky Chilcott


Jordan Raine

unread,
Mar 19, 2014, 1:10:56 AM3/19/14
to Ricky Chilcott, munkise...@googlegroups.com
That looks great. Yes, I think each token should have same rights as user. 

Having these tokens will open up some cool API possibilities! 

Jordan

Sent from my iPhone

Jeppe Ernst

unread,
Apr 7, 2014, 5:30:45 AM4/7/14
to munkise...@googlegroups.com
Hi Ronald

We're using this script https://github.com/mkuron/deploystudio_remote_scripts (we use a slightly modified version to support token <> group relations & email reporting).
It's pretty easy to get up and running if you need a solution ASAP.

-Jeppe
Reply all
Reply to author
Forward
0 new messages