github.com/smugmug/godynamo@v0.0.0-20151122084750-7913028f6623/conf_iam/conf_iam.go (about)

     1  // Manages writing roles credentials into the global conf.Vals state.
     2  //
     3  // example use:
     4  //
     5  //   iam_ready_chan := make(chan bool)
     6  //   go conf_iam.GoIAM(iam_ready_chan)
     7  //   iam_ready := <- iam_ready_chan
     8  //   if iam_ready {
     9  //	fmt.Printf("using iam\n")
    10  //   } else {
    11  //	fmt.Printf("not using iam\n")
    12  //   }
    13  package conf_iam
    14  
    15  import (
    16  	"errors"
    17  	"fmt"
    18  	conf "github.com/smugmug/godynamo/conf"
    19  	roles_files "github.com/smugmug/goawsroles/roles_files"
    20  	"log"
    21  	"time"
    22  )
    23  
    24  // AssignCredentialsToConf will safely copy the credentials data from rf to the conf c.
    25  func AssignCredentialsToConf(rf *roles_files.RolesFiles, c *conf.AWS_Conf) error {
    26  	if rf == nil || c == nil {
    27  		return errors.New("conf_iam.AssignCredentialsToConf: rf or c is nil")
    28  	}
    29  	accessKey, secret, token, get_err := rf.Get()
    30  	if get_err != nil {
    31  		e := fmt.Sprintf("conf_iam.AssignCredentialsToConf:cannot get a role file:%s",
    32  			get_err.Error())
    33  		return errors.New(e)
    34  	}
    35  	c.ConfLock.Lock()
    36  	c.IAM.Credentials.AccessKey = accessKey
    37  	c.IAM.Credentials.Secret = secret
    38  	c.IAM.Credentials.Token = token
    39  	c.ConfLock.Unlock()
    40  	e := fmt.Sprintf("IAM credentials assigned at %v", time.Now())
    41  	log.Printf(e)
    42  	return nil
    43  }
    44  
    45  // AssignCredentials will safely copy the credentials data from rf to the global conf.Vals.
    46  func AssignCredentials(rf *roles_files.RolesFiles) error {
    47  	return AssignCredentialsToConf(rf, &conf.Vals)
    48  }
    49  
    50  // ReadIAMToConf will read the credentials data from rf and safely assign it to conf c.
    51  func ReadIAMToConf(rf *roles_files.RolesFiles, c *conf.AWS_Conf) error {
    52  	if rf == nil || c == nil {
    53  		return errors.New("conf_iam.ReadIAMToConf: rf or c is nil")
    54  	}
    55  	roles_read_err := rf.RolesRead()
    56  	if roles_read_err != nil {
    57  		e := fmt.Sprintf("conf_iam.ReadIAM:cannot perform initial roles read: %s",
    58  			roles_read_err.Error())
    59  		return errors.New(e)
    60  	}
    61  	return AssignCredentialsToConf(rf, c)
    62  }
    63  
    64  // ReadIAM will read the credentials data from rf and safely assign it to the global conf.Vals.
    65  func ReadIAM(rf *roles_files.RolesFiles) error {
    66  	return ReadIAMToConf(rf, &conf.Vals)
    67  }
    68  
    69  // WatchIAMToConf will begin rf's RolesWatch method and wait to receive signals that new credentials
    70  // are available to be assigned, which will then be safely copied to conf c.
    71  func WatchIAMToConf(rf *roles_files.RolesFiles, c *conf.AWS_Conf, watch_err_chan chan error) {
    72  	if rf == nil || c == nil {
    73  		watch_err_chan <- errors.New("conf_iam.WatchIAMToConf: rf or c is nil")
    74  		return
    75  	}
    76  	err_chan := make(chan error)
    77  	read_signal := make(chan bool)
    78  	go rf.RolesWatch(err_chan, read_signal)
    79  	e := "IAM watching set to true, waiting..."
    80  	log.Printf(e)
    81  	for {
    82  		select {
    83  		case roles_watch_err := <-err_chan:
    84  			watch_err_chan <- roles_watch_err
    85  		case <-read_signal:
    86  			e := "WatchIAM received a read signal"
    87  			log.Printf(e)
    88  			assign_err := AssignCredentialsToConf(rf, c)
    89  			if assign_err != nil {
    90  				watch_err_chan <- assign_err
    91  			}
    92  		}
    93  	}
    94  }
    95  
    96  // WatchIAMToConf will begin rf's RolesWatch method and wait to receive signals that new credentials
    97  // are available to be assigned, which will then be safely copied to the global conf.Vals.
    98  func WatchIAM(rf *roles_files.RolesFiles, watch_err_chan chan error) {
    99  	WatchIAMToConf(rf, &conf.Vals, watch_err_chan)
   100  }
   101  
   102  // GoIAMToConf is a convenience wrapper for callers using roles_files instantiation of the roles interface.
   103  // First there is a blocking read on the roles files to get the initial roles information. Then the
   104  // file notification watcher will run as a goroutine, and resetting conf c's roles
   105  // values. If IAM Credentials are ready for use, the parameter chan `ready_chan` will receive a true
   106  // value, otherwise false. A false value on this chan should indicate to a caller that another auth
   107  // mechanism (for example, hardocded credentials) should be used.
   108  func GoIAMToConf(c *conf.AWS_Conf, ready_chan chan bool) {
   109  	if c == nil {
   110  		log.Printf("conf_iam.GoIAMToConf: c is nil")
   111  		ready_chan <- false
   112  		return
   113  	}
   114  	use_iam := false
   115  	c.ConfLock.RLock()
   116  	use_iam = c.UseIAM
   117  	c.ConfLock.RUnlock()
   118  	if use_iam == true {
   119  		rf := roles_files.NewRolesFiles()
   120  		watching := false
   121  		c.ConfLock.RLock()
   122  		rf.BaseDir = c.IAM.File.BaseDir
   123  		rf.AccessKeyFile = c.IAM.File.AccessKey
   124  		rf.SecretFile = c.IAM.File.Secret
   125  		rf.TokenFile = c.IAM.File.Token
   126  		watching = c.IAM.Watch
   127  		c.ConfLock.RUnlock()
   128  		roles_read_err := ReadIAMToConf(rf, c)
   129  		if roles_read_err != nil {
   130  			e := fmt.Sprintf("conf_iam.GoIAMToConf:cannot perform initial roles read: %s",
   131  				roles_read_err.Error())
   132  			log.Printf(e)
   133  			c.ConfLock.Lock()
   134  			c.UseIAM = false
   135  			c.ConfLock.Unlock()
   136  			ready_chan <- false
   137  			return
   138  		}
   139  		// signal to caller that iam roles are ready to use
   140  		ready_chan <- true
   141  		if watching == true {
   142  			watch_err := make(chan error)
   143  			go WatchIAMToConf(rf, c, watch_err)
   144  			go func() {
   145  				select {
   146  				case err := <-watch_err:
   147  					if err != nil {
   148  						log.Printf(err.Error())
   149  						// caller can fall back to hard-coded perms
   150  						// or live with the panic
   151  						c.ConfLock.Lock()
   152  						c.UseIAM = false
   153  						c.ConfLock.Unlock()
   154  					}
   155  				}
   156  			}()
   157  		}
   158  	} else {
   159  		// signal to the caller than iam roles are not selected as a auth mechanism
   160  		e := fmt.Sprintf("conf_iam.GoIAMToConf: not using IAM")
   161  		log.Printf(e)
   162  		ready_chan <- false
   163  	}
   164  }
   165  
   166  // GoIAM calls the GoIAMToConf credential manager, assigning to the global conf.Vals.
   167  func GoIAM(ready_chan chan bool) {
   168  	GoIAMToConf(&conf.Vals, ready_chan)
   169  }