Skip to content

Commit 67bada2

Browse files
committed
added admin unix socket and fixed docker arch on build
1 parent d68d972 commit 67bada2

File tree

2 files changed

+153
-11
lines changed

2 files changed

+153
-11
lines changed

build-docker.sh

+5-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#!/bin/bash -e
22

3-
VERSION="0.0.1"
3+
VERSION="0.0.5"
44

55
rm -rf dockerbuild || true
66
mkdir dockerbuild
@@ -9,6 +9,10 @@ cp Dockerfile dockerbuild/Dockerfile-amd64
99
cp Dockerfile dockerbuild/Dockerfile-arm
1010
cp Dockerfile dockerbuild/Dockerfile-arm64
1111

12+
sed -E 's|FROM alpine|FROM amd64/alpine|' -i dockerbuild/Dockerfile-amd64
13+
sed -E 's|FROM alpine|FROM arm32v7/alpine|' -i dockerbuild/Dockerfile-arm
14+
sed -E 's|FROM alpine|FROM arm64v8/alpine|' -i dockerbuild/Dockerfile-arm64
15+
1216
sed -E 's/GOARCH=/GOARCH=amd64/' -i dockerbuild/Dockerfile-amd64
1317
sed -E 's/GOARCH=/GOARCH=arm/' -i dockerbuild/Dockerfile-arm
1418
sed -E 's/GOARCH=/GOARCH=arm64/' -i dockerbuild/Dockerfile-arm64

main.go

+148-10
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package main
22

33
import (
4+
"bytes"
45
"crypto/tls"
56
"crypto/x509"
67
"encoding/json"
@@ -17,6 +18,7 @@ import (
1718
"path/filepath"
1819
"strings"
1920
"sync"
21+
"time"
2022

2123
tunnel "git.sequentialread.com/forest/tunnel/tunnel-lib"
2224
)
@@ -42,10 +44,11 @@ type ClientConfig struct {
4244
ClientIdentifier string
4345
ServerAddr string
4446
UseTls bool
45-
ServiceToLocalAddrMap map[string]string
47+
ServiceToLocalAddrMap *map[string]string
4648
CaCertificateFilesGlob string
4749
ClientTlsKeyFile string
4850
ClientTlsCertificateFile string
51+
AdminUnixSocket string
4952
}
5053

5154
type ListenerConfig struct {
@@ -66,6 +69,13 @@ type ManagementHttpHandler struct {
6669
ControlHandler http.Handler
6770
}
6871

72+
type LiveConfigUpdate struct {
73+
Listeners []ListenerConfig
74+
ServiceToLocalAddrMap map[string]string
75+
}
76+
77+
type adminAPI struct{}
78+
6979
// Server State
7080
var listeners []ListenerConfig
7181
var clientStatesMutex = &sync.Mutex{}
@@ -74,6 +84,9 @@ var server *tunnel.Server
7484

7585
// Client State
7686
var client *tunnel.Client
87+
var tlsClientConfig *tls.Config
88+
var serverURL *string
89+
var serviceToLocalAddrMap *map[string]string
7790

7891
func main() {
7992

@@ -94,6 +107,88 @@ func main() {
94107

95108
}
96109

110+
// admin api handler for /liveconfig over unix socket
111+
func (handler adminAPI) ServeHTTP(response http.ResponseWriter, request *http.Request) {
112+
switch path.Clean(request.URL.Path) {
113+
case "/liveconfig":
114+
if request.Method == "PUT" {
115+
requestBytes, err := ioutil.ReadAll(request.Body)
116+
if err != nil {
117+
log.Printf("adminAPI: request read error: %+v\n\n", err)
118+
http.Error(response, "500 request read error", http.StatusInternalServerError)
119+
return
120+
}
121+
var configUpdate LiveConfigUpdate
122+
err = json.Unmarshal(requestBytes, &configUpdate)
123+
if err != nil {
124+
log.Printf("adminAPI: can't parse JSON: %+v\n\n", err)
125+
http.Error(response, "400 bad request: can't parse JSON", http.StatusBadRequest)
126+
return
127+
}
128+
129+
sendBytes, err := json.Marshal(configUpdate.Listeners)
130+
if err != nil {
131+
log.Printf("adminAPI: Listeners json serialization failed: %+v\n\n", err)
132+
http.Error(response, "500 Listeners json serialization failed", http.StatusInternalServerError)
133+
return
134+
}
135+
apiURL := fmt.Sprintf("https://%s/tunnels", *serverURL)
136+
tunnelsRequest, err := http.NewRequest("PUT", apiURL, bytes.NewReader(sendBytes))
137+
if err != nil {
138+
log.Printf("adminAPI: error creating tunnels request: %+v\n\n", err)
139+
http.Error(response, "500 error creating tunnels request", http.StatusInternalServerError)
140+
return
141+
}
142+
tunnelsRequest.Header.Add("content-type", "application/json")
143+
144+
client := &http.Client{
145+
Transport: &http.Transport{
146+
TLSClientConfig: tlsClientConfig,
147+
},
148+
Timeout: 10 * time.Second,
149+
}
150+
tunnelsResponse, err := client.Do(tunnelsRequest)
151+
if err != nil {
152+
log.Printf("adminAPI: Do(tunnelsRequest): %+v\n\n", err)
153+
http.Error(response, "502 tunnels request failed", http.StatusBadGateway)
154+
return
155+
}
156+
tunnelsResponseBytes, err := ioutil.ReadAll(tunnelsResponse.Body)
157+
if err != nil {
158+
log.Printf("adminAPI: tunnelsResponse read error: %+v\n\n", err)
159+
http.Error(response, "502 tunnelsResponse read error", http.StatusBadGateway)
160+
return
161+
}
162+
163+
if tunnelsResponse.StatusCode != http.StatusOK {
164+
log.Printf(
165+
"adminAPI: tunnelsRequest returned HTTP %d: %s\n\n",
166+
tunnelsResponse.StatusCode, string(tunnelsResponseBytes),
167+
)
168+
http.Error(
169+
response,
170+
fmt.Sprintf("502 tunnels request returned HTTP %d", tunnelsResponse.StatusCode),
171+
http.StatusBadGateway,
172+
)
173+
return
174+
}
175+
176+
serviceToLocalAddrMap = &configUpdate.ServiceToLocalAddrMap
177+
178+
response.Header().Add("content-type", "application/json")
179+
response.WriteHeader(http.StatusOK)
180+
response.Write(tunnelsResponseBytes)
181+
182+
} else {
183+
response.Header().Set("Allow", "PUT")
184+
http.Error(response, "405 method not allowed, try PUT", http.StatusMethodNotAllowed)
185+
}
186+
default:
187+
http.Error(response, "404 not found, try PUT /liveconfig", http.StatusNotFound)
188+
}
189+
190+
}
191+
97192
func runClient(configFileName *string) {
98193

99194
configBytes := getConfigBytes(configFileName)
@@ -103,13 +198,16 @@ func runClient(configFileName *string) {
103198
if err != nil {
104199
log.Fatalf("runClient(): can't json.Unmarshal(configBytes, &config) because %s \n", err)
105200
}
201+
serviceToLocalAddrMap = config.ServiceToLocalAddrMap
202+
serverURL = &config.ServerAddr
106203

107204
configToLog, _ := json.MarshalIndent(config, "", " ")
108205
log.Printf("theshold client is starting up using config:\n%s\n", string(configToLog))
109206

110207
dialFunction := net.Dial
111208

112209
if config.UseTls {
210+
113211
cert, err := tls.LoadX509KeyPair(config.ClientTlsCertificateFile, config.ClientTlsKeyFile)
114212
if err != nil {
115213
log.Fatal(err)
@@ -129,41 +227,81 @@ func runClient(configFileName *string) {
129227
caCertPool.AppendCertsFromPEM(caCert)
130228
}
131229

132-
tlsConfig := &tls.Config{
230+
tlsClientConfig = &tls.Config{
133231
Certificates: []tls.Certificate{cert},
134232
RootCAs: caCertPool,
135233
}
136-
tlsConfig.BuildNameToCertificate()
234+
tlsClientConfig.BuildNameToCertificate()
137235

138236
dialFunction = func(network, address string) (net.Conn, error) {
139-
return tls.Dial(network, address, tlsConfig)
237+
return tls.Dial(network, address, tlsClientConfig)
140238
}
141239
}
142240

241+
clientStateChanges := make(chan *tunnel.ClientStateChange)
143242
tunnelClientConfig := &tunnel.ClientConfig{
144243
DebugLog: config.DebugLog,
145244
Identifier: config.ClientIdentifier,
146245
ServerAddr: config.ServerAddr,
147246
FetchLocalAddr: func(service string) (string, error) {
148-
localAddr, hasLocalAddr := config.ServiceToLocalAddrMap[service]
247+
//log.Printf("(*serviceToLocalAddrMap): %+v\n\n", (*serviceToLocalAddrMap))
248+
localAddr, hasLocalAddr := (*serviceToLocalAddrMap)[service]
149249
if !hasLocalAddr {
150250
return "", errors.New("service not configured. See ServiceToLocalAddrMap in client config file.")
151251
}
152252
return localAddr, nil
153253
},
154-
Dial: dialFunction,
254+
Dial: dialFunction,
255+
StateChanges: clientStateChanges,
155256
}
156257

157258
client, err = tunnel.NewClient(tunnelClientConfig)
158259
if err != nil {
159260
log.Fatalf("runClient(): can't create tunnel client because %s \n", err)
160261
}
161262

263+
go (func() {
264+
for {
265+
stateChange := <-clientStateChanges
266+
fmt.Printf("clientStateChange: %s\n", stateChange.String())
267+
}
268+
})()
269+
270+
go runClientAdminApi(config)
271+
162272
fmt.Print("runClient(): the client should be running now\n")
163273
client.Start()
164274

165275
}
166276

277+
func runClientAdminApi(config ClientConfig) {
278+
279+
os.Remove(config.AdminUnixSocket)
280+
281+
listenAddress, err := net.ResolveUnixAddr("unix", config.AdminUnixSocket)
282+
if err != nil {
283+
panic(fmt.Sprintf("runClient(): can't start because net.ResolveUnixAddr() returned %+v", err))
284+
}
285+
286+
listener, err := net.ListenUnix("unix", listenAddress)
287+
if err != nil {
288+
panic(fmt.Sprintf("can't start because net.ListenUnix() returned %+v", err))
289+
}
290+
log.Printf("AdminUnixSocket Listening: %v\n\n", config.AdminUnixSocket)
291+
defer listener.Close()
292+
293+
server := http.Server{
294+
Handler: adminAPI{},
295+
ReadTimeout: 10 * time.Second,
296+
WriteTimeout: 10 * time.Second,
297+
}
298+
299+
err = server.Serve(listener)
300+
if err != nil {
301+
panic(fmt.Sprintf("AdminUnixSocket server returned %+v", err))
302+
}
303+
}
304+
167305
func runServer(configFileName *string) {
168306

169307
configBytes := getConfigBytes(configFileName)
@@ -345,8 +483,8 @@ func compareListenerConfigs(a, b ListenerConfig) bool {
345483

346484
func (s *ManagementHttpHandler) ServeHTTP(responseWriter http.ResponseWriter, request *http.Request) {
347485

348-
switch fmt.Sprintf("%s/", path.Clean(request.URL.Path)) {
349-
case "/clients/":
486+
switch path.Clean(request.URL.Path) {
487+
case "/clients":
350488
if request.Method == "GET" {
351489
clientStatesMutex.Lock()
352490
bytes, err := json.Marshal(clientStates)
@@ -362,7 +500,7 @@ func (s *ManagementHttpHandler) ServeHTTP(responseWriter http.ResponseWriter, re
362500
responseWriter.Header().Set("Allow", "PUT")
363501
http.Error(responseWriter, "405 Method Not Allowed", http.StatusMethodNotAllowed)
364502
}
365-
case "/tunnels/":
503+
case "/tunnels":
366504
if request.Method == "PUT" {
367505
if request.Header.Get("Content-Type") != "application/json" {
368506
http.Error(responseWriter, "415 Unsupported Media Type: Content-Type must be application/json", http.StatusUnsupportedMediaType)
@@ -399,7 +537,7 @@ func (s *ManagementHttpHandler) ServeHTTP(responseWriter http.ResponseWriter, re
399537
responseWriter.Header().Set("Allow", "PUT")
400538
http.Error(responseWriter, "405 Method Not Allowed", http.StatusMethodNotAllowed)
401539
}
402-
case "/ping/":
540+
case "/ping":
403541
if request.Method == "GET" {
404542
fmt.Fprint(responseWriter, "pong")
405543
} else {

0 commit comments

Comments
 (0)