|
| 1 | +package server |
| 2 | + |
| 3 | +import ( |
| 4 | + "fmt" |
| 5 | + "runtime" |
| 6 | + "time" |
| 7 | + |
| 8 | + "github.com/dan-v/awslambdaproxy/pkg/server/publicip" |
| 9 | + "github.com/dan-v/awslambdaproxy/pkg/server/publicip/awspublicip" |
| 10 | + "github.com/sirupsen/logrus" |
| 11 | +) |
| 12 | + |
| 13 | +const ( |
| 14 | + // LambdaMinMemorySize is the minimum memory size for a Lambda function in MB |
| 15 | + LambdaMinMemorySize = 128 |
| 16 | + // LambdaMaxMemorySize is the maximum memory size for a Lambda function in MB |
| 17 | + LambdaMaxMemorySize = 3008 |
| 18 | + |
| 19 | + // LambdaDelayedCleanupTime is the time to wait if active connections still exist |
| 20 | + // this should be never be higher than LambdaExecutionTimeoutBuffer or function timeout |
| 21 | + // will happen before cleanup occurs |
| 22 | + LambdaDelayedCleanupTime = time.Second * 20 |
| 23 | + // LambdaExecutionTimeoutBuffer is the time added to user specified execution frequency |
| 24 | + // to get the overall function timeout value |
| 25 | + LambdaExecutionTimeoutBuffer = time.Second * 30 |
| 26 | + |
| 27 | + // LambdaMinExecutionFrequency is the minimum frequency for function execution. |
| 28 | + LambdaMinExecutionFrequency = time.Second * 60 |
| 29 | + // LambdaMaxExecutionFrequency is the maximum frequency for function execution. |
| 30 | + // The current max execution time is 900 seconds, but this takes into account |
| 31 | + // LambdaExecutionTimeoutBuffer + 10 seconds of leeway |
| 32 | + LambdaMaxExecutionFrequency = time.Second * 860 |
| 33 | +) |
| 34 | + |
| 35 | +// Config is used to define the configuration for Server |
| 36 | +type Config struct { |
| 37 | + // LambdaRegions is all regions to execute Lambda functions in |
| 38 | + LambdaRegions []string |
| 39 | + // LambdaMemory is the size of memory to assign Lambda function |
| 40 | + LambdaMemory int |
| 41 | + // LambdaExecutionFrequency is the frequency at which to execute Lambda functions |
| 42 | + LambdaExecutionFrequency time.Duration |
| 43 | + // ProxyListeners defines all listeners, protocol, and auth information |
| 44 | + // in format like this [scheme://][user:pass@host]:port. |
| 45 | + // see https://github.com/ginuerzh/gost/blob/master/README_en.md#getting-started |
| 46 | + ProxyListeners []string |
| 47 | + // ProxyDebug is whether debug logging should be shown for proxy traffic |
| 48 | + // note this will log all visited domains |
| 49 | + ProxyDebug bool |
| 50 | + // ReverseTunnelSSHUser is the ssh user to use for the lambda reverse ssh tunnel |
| 51 | + ReverseTunnelSSHUser string |
| 52 | + // ReverseTunnelSSHPort is the ssh port to use for the lambda reverse ssh tunnel |
| 53 | + ReverseTunnelSSHPort string |
| 54 | + // Debug enables general debug logging |
| 55 | + Debug bool |
| 56 | +} |
| 57 | + |
| 58 | +// Server is the long running server component of awslambdaproxy |
| 59 | +type Server struct { |
| 60 | + publicIPClient publicip.Client |
| 61 | + lambdaRegions []string |
| 62 | + lambdaMemory int64 |
| 63 | + lambdaExecutionFrequency time.Duration |
| 64 | + lambdaTimeoutSeconds int64 |
| 65 | + proxyListeners []string |
| 66 | + proxyDebug bool |
| 67 | + reverseTunnelSSHUser string |
| 68 | + reverseTunnelSSHPort string |
| 69 | + debug bool |
| 70 | + logger *logrus.Logger |
| 71 | +} |
| 72 | + |
| 73 | +func New(config Config) (*Server, error) { |
| 74 | + logger := logrus.New() |
| 75 | + if config.Debug { |
| 76 | + logger.SetLevel(logrus.DebugLevel) |
| 77 | + } |
| 78 | + |
| 79 | + err := validateConfig(config) |
| 80 | + if err != nil { |
| 81 | + return nil, err |
| 82 | + } |
| 83 | + |
| 84 | + functionTimeout := int(config.LambdaExecutionFrequency.Seconds()) + int(LambdaExecutionTimeoutBuffer.Seconds()) |
| 85 | + s := &Server{ |
| 86 | + publicIPClient: awspublicip.New(), |
| 87 | + lambdaRegions: config.LambdaRegions, |
| 88 | + lambdaMemory: int64(config.LambdaMemory), |
| 89 | + lambdaExecutionFrequency: config.LambdaExecutionFrequency, |
| 90 | + lambdaTimeoutSeconds: int64(functionTimeout), |
| 91 | + proxyListeners: config.ProxyListeners, |
| 92 | + proxyDebug: config.ProxyDebug, |
| 93 | + reverseTunnelSSHUser: config.ReverseTunnelSSHUser, |
| 94 | + reverseTunnelSSHPort: config.ReverseTunnelSSHPort, |
| 95 | + debug: config.Debug, |
| 96 | + logger: logger, |
| 97 | + } |
| 98 | + |
| 99 | + logger.WithFields(logrus.Fields{ |
| 100 | + "publicIPClient": s.publicIPClient.ProviderURL(), |
| 101 | + "lambdaRegions": s.lambdaRegions, |
| 102 | + "lambdaMemory": s.lambdaMemory, |
| 103 | + "lambdaExecutionFrequency": s.lambdaExecutionFrequency, |
| 104 | + "lambdaTimeoutSeconds": s.lambdaTimeoutSeconds, |
| 105 | + "proxyListeners": s.proxyListeners, |
| 106 | + "proxyDebug": s.proxyDebug, |
| 107 | + "reverseTunnelSSHUser": s.reverseTunnelSSHUser, |
| 108 | + "reverseTunnelSSHPort": s.reverseTunnelSSHPort, |
| 109 | + "debug": s.debug, |
| 110 | + }).Info("server has been configured with the following values") |
| 111 | + |
| 112 | + return s, nil |
| 113 | +} |
| 114 | + |
| 115 | +func (s *Server) Run() { |
| 116 | + publicIP, err := s.publicIPClient.GetIP() |
| 117 | + if err != nil { |
| 118 | + s.logger.WithError(err).Fatalf("error getting public IP address") |
| 119 | + } |
| 120 | + |
| 121 | + s.logger.Infof("setting up lambda infrastructure") |
| 122 | + err = setupLambdaInfrastructure(s.lambdaRegions, s.lambdaMemory, s.lambdaTimeoutSeconds) |
| 123 | + if err != nil { |
| 124 | + s.logger.WithError(err).Fatalf("failed to setup lambda infrastructure") |
| 125 | + } |
| 126 | + |
| 127 | + s.logger.Infof("starting ssh tunnel manager") |
| 128 | + privateKey, err := NewSSHManager() |
| 129 | + if err != nil { |
| 130 | + s.logger.WithError(err).Fatalf("failed to setup ssh tunnel manager") |
| 131 | + } |
| 132 | + |
| 133 | + s.logger.Infof("starting local proxy") |
| 134 | + localProxy, err := NewLocalProxy(s.proxyListeners, s.proxyDebug) |
| 135 | + if err != nil { |
| 136 | + s.logger.WithError(err).Fatalf("failed to setup local proxy") |
| 137 | + } |
| 138 | + |
| 139 | + s.logger.Println("starting connection manager") |
| 140 | + tunnelConnectionManager, err := newTunnelConnectionManager(s.lambdaExecutionFrequency, localProxy) |
| 141 | + if err != nil { |
| 142 | + s.logger.WithError(err).Fatalf("failed to setup connection manager") |
| 143 | + } |
| 144 | + |
| 145 | + s.logger.Println("starting lambda execution manager") |
| 146 | + _, err = newLambdaExecutionManager(publicIP, s.lambdaRegions, s.lambdaExecutionFrequency, |
| 147 | + s.reverseTunnelSSHUser, s.reverseTunnelSSHPort, privateKey, tunnelConnectionManager.tunnelRedeployNeeded) |
| 148 | + if err != nil { |
| 149 | + s.logger.WithError(err).Fatalf("failed to setup lambda execution manager") |
| 150 | + } |
| 151 | + |
| 152 | + s.logger.Println("#######################################") |
| 153 | + s.logger.Println("proxy ip address: ", publicIP) |
| 154 | + s.logger.Println("listeners: ", s.proxyListeners) |
| 155 | + s.logger.Println("#######################################") |
| 156 | + |
| 157 | + runtime.Goexit() |
| 158 | +} |
| 159 | + |
| 160 | +func validateConfig(config Config) error { |
| 161 | + // validate memory |
| 162 | + if config.LambdaMemory < LambdaMinMemorySize || config.LambdaMemory > LambdaMaxMemorySize { |
| 163 | + return fmt.Errorf("invalid lambda memory size '%vMB' - should be between %v and %v", |
| 164 | + config.LambdaMemory, LambdaMinMemorySize, LambdaMaxMemorySize) |
| 165 | + } |
| 166 | + if config.LambdaMemory%64 != 0 { |
| 167 | + return fmt.Errorf("invalid lambda memory size '%vMB' - should be in increments of 64MB", |
| 168 | + config.LambdaMemory) |
| 169 | + } |
| 170 | + |
| 171 | + // validate frequency |
| 172 | + if config.LambdaExecutionFrequency < LambdaMinExecutionFrequency || config.LambdaExecutionFrequency > LambdaMaxExecutionFrequency { |
| 173 | + return fmt.Errorf("invalid lambda execution frequency '%v' - should be between %v and %v", |
| 174 | + config.LambdaExecutionFrequency, LambdaMinExecutionFrequency, LambdaMaxExecutionFrequency) |
| 175 | + } |
| 176 | + |
| 177 | + // validate ssh user and port |
| 178 | + if config.ReverseTunnelSSHUser == "" { |
| 179 | + return fmt.Errorf("need to specify ReverseTunnelSSHUser") |
| 180 | + } |
| 181 | + if config.ReverseTunnelSSHPort == "" { |
| 182 | + return fmt.Errorf("need to specify ReverseTunnelSSHPort") |
| 183 | + } |
| 184 | + |
| 185 | + // validate listeners |
| 186 | + if len(config.ProxyListeners) == 0 { |
| 187 | + return fmt.Errorf("no listener has been specified") |
| 188 | + } |
| 189 | + |
| 190 | + // validate regions |
| 191 | + validRegions := GetValidLambdaRegions() |
| 192 | + for _, region := range config.LambdaRegions { |
| 193 | + valid := false |
| 194 | + for _, validRegion := range validRegions { |
| 195 | + if region == validRegion { |
| 196 | + valid = true |
| 197 | + break |
| 198 | + } |
| 199 | + } |
| 200 | + if !valid { |
| 201 | + return fmt.Errorf("invalid region '%v' specified. valid regions: %v", |
| 202 | + region, validRegions) |
| 203 | + } |
| 204 | + } |
| 205 | + return nil |
| 206 | +} |
0 commit comments