2
votes

I want to use Salt Stack to manage cloud servers that will be automatically created by another application. I can't use salt-cloud to create the new servers and boot strap them because another application that I don't have control over will be automatically creating and deleting them.

What I can do is build the image that the application will use when it creates and deletes new cloud server instances.

What I'm looking for is a way for a newly created minion to boot strap itself to the salt master without the salt-master knowing anything about it before it's created.

The issue I have is that I need a way to pre-seed the keys so that the new minions can get be accepted automatically.

I was trying to use salt-api to do this by having a script run at boot which would connect to the salt-master and generate a new key for itself. The new minions could use pre-seeded keys so that it connects automatically to the master.

But I'm having issue trying to figure out how to call salt-key from the salt-api in order to generate a new key each time a minion is created.

3

3 Answers

0
votes

If you are able to firewall your master (i.e. all minions share some secure subnets), you could go the easy way instead and use Salts auto_accept setting (or the autosign_file/autoreject_file settings right below).

The minions authenticating to the master and accepting their own keys is a rather big security issue. I know of no way to limit access within the salt api, which would mean a minion could not only accept keys, but also manipulate every other minion.

0
votes

You can use salt-api to do this but you will need to setup the appropriate reactor code to accept the key. I've been working on a Salt-API + Reactor formula for some of my projects and I included a minion accept key end point with my configuration.

You can see minion key accept reactor config here.

I'm assuming you already have salt-api setup and accepting webhooks from your original question.

0
votes

With python-request you can create a function like this:

import requests
import os


def accept_salt_clientkey(keyname):
    url = 'https://saltmaster:8000'
    headers = {'Accept':'application/json'}
    login_payload = {'username':'saltuser','password':'saltpasswd','eauth':'pam'}
    accept_key_payload = {'fun': 'key.accept','client':'wheel','tgt':'*','match':keyname}

    login_request = requests.post(os.path.join(url,'login'),headers=headers,data=login_payload)
    request = requests.post(url,headers=headers,data=accept_key_payload,cookies=login_request.cookies)
    keytype = request.json()['return'][0]['data']['return']
    if keytype:
        for key,value in keytype.iteritems():
            if value[0] == keyname:
                return True
                break
            else:
                raise Exception('{} does not match!'.format(keyname))
    else:
        raise Exception('{} key does not exist in master until now...'.format(keyname)) 

And you can call it using the hostname (which should be the keyname) as an argument:

accept_salt_clientkey(mynewhost.com)

Of course you could store username/password in a config.py file or another way depending on how you will call it, but this may give you a general idea.