Skip to content

Commit

Permalink
Added TLS support
Browse files Browse the repository at this point in the history
Signed-off-by: Philip Schmid <[email protected]>
  • Loading branch information
PhilipSchmid committed Jul 30, 2024
1 parent 2c57358 commit 65d2ce8
Show file tree
Hide file tree
Showing 2 changed files with 106 additions and 3 deletions.
21 changes: 21 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ Tiny golang app which returns a timestamp, a customizable message, the hostname,
- `NODE`: The name of the node where the app is running. This is typically used in a Kubernetes environment.
- `PORT`: The port number on which the server listens. Default is `8080`.
- `PRINT_HTTP_REQUEST_HEADERS`: Set to `true` to include HTTP request headers in the JSON response. By default, headers are not included.
- `TLS`: Set to `true` to enable TLS (HTTPS) support. By default, TLS is disabled.
- `TLS_PORT`: The port number on which the TLS server listens. Default is `8443`.

## Standalone Container
Shell 1 (server):
Expand All @@ -20,6 +22,8 @@ docker run -it -p 8080:8080 -e MESSAGE="demo-env" ghcr.io/philipschmid/echo-app:
docker run -it -p 8080:8080 -e NODE="k8s-node-1" ghcr.io/philipschmid/echo-app:main
# Optionally include HTTP request headers in the response:
docker run -it -p 8080:8080 -e PRINT_HTTP_REQUEST_HEADERS="true" ghcr.io/philipschmid/echo-app:main
# Optionally enable TLS:
docker run -it -p 8080:8080 -p 8443:8443 -e TLS="true" ghcr.io/philipschmid/echo-app:main
```

Shell 2 (client):
Expand All @@ -38,6 +42,11 @@ If `PRINT_HTTP_REQUEST_HEADERS` is set to `true`, the response will also include
{"timestamp":"2024-05-28T20:21:23.363Z","hostname":"3f96391b04f2","source_ip":"192.168.65.1","node":"k8s-node-1","headers":{"Accept":["*/*"],"User-Agent":["curl/8.6.0"]}}
```

If `TLS` is enabled, you can test the HTTPS endpoint:
```bash
curl -k https://localhost:8443/
```

## Kubernetes
Apply the following manifests to deploy the echo-app with the `NODE` environment variable set to the name of the Kubernetes node using the Downward API:
```yaml
Expand All @@ -50,6 +59,8 @@ data:
MESSAGE: "demo-env"
# Add the PRINT_HTTP_REQUEST_HEADERS key with a value of "true" to include headers in the response
PRINT_HTTP_REQUEST_HEADERS: "true"
# Add the TLS key with a value of "true" to enable TLS
TLS: "true"
---
apiVersion: apps/v1
kind: Deployment
Expand Down Expand Up @@ -82,6 +93,7 @@ spec:
image: ghcr.io/philipschmid/echo-app:main
ports:
- containerPort: 8080
- containerPort: 8443
env:
- name: MESSAGE
valueFrom:
Expand All @@ -94,6 +106,12 @@ spec:
configMapKeyRef:
name: echo-app-config
key: PRINT_HTTP_REQUEST_HEADERS
# Add the TLS environment variable
- name: TLS
valueFrom:
configMapKeyRef:
name: echo-app-config
key: TLS
# Add the NODE environment variable using the downward API
- name: NODE
valueFrom:
Expand All @@ -111,6 +129,9 @@ spec:
- protocol: TCP
port: 8080
targetPort: 8080
- protocol: TCP
port: 8443
targetPort: 8443
type: ClusterIP
```
Expand Down
88 changes: 85 additions & 3 deletions main.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
package main

import (
"crypto/rand"
"crypto/rsa"
"crypto/tls"
"crypto/x509"
"crypto/x509/pkix"
"encoding/json"
"encoding/pem"
"log"
"math/big"
"net"
"net/http"
"os"
Expand All @@ -21,10 +28,11 @@ type Response struct {
}

func main() {
// Get MESSAGE and PRINT_HTTP_REQUEST_HEADERS environment variables
// Get MESSAGE, NODE, PRINT_HTTP_REQUEST_HEADERS, and TLS environment variables
messagePtr := getMessagePtr()
nodePtr := getNodePtr()
printHeaders := getPrintHeadersSetting()
tlsEnabled := getTLSSetting()

// Prepare the message log
messageLog := "No MESSAGE environment variable set"
Expand All @@ -47,6 +55,11 @@ func main() {
} else {
log.Println(" PRINT_HTTP_REQUEST_HEADERS is disabled")
}
if tlsEnabled {
log.Println(" TLS is enabled")
} else {
log.Println(" TLS is disabled")
}

// Register hello function to handle all requests
mux := http.NewServeMux()
Expand All @@ -59,8 +72,41 @@ func main() {
}

// Start the web server on port and accept requests
log.Printf("Server listening on port %s\n", port)
log.Fatal(http.ListenAndServe(":"+port, mux))
go func() {
log.Printf("Server listening on port %s\n", port)
log.Fatal(http.ListenAndServe(":"+port, mux))
}()

if tlsEnabled {
// Use TLS_PORT environment variable, or default to 8443
tlsPort := os.Getenv("TLS_PORT")
if tlsPort == "" {
tlsPort = "8443"
}

// Generate in-memory TLS certificate pair
cert, err := generateSelfSignedCert()
if err != nil {
log.Fatalf("Failed to generate self-signed certificate: %v", err)
}

// Start the HTTPS server on the specified TLS port
go func() {
server := &http.Server{
Addr: ":" + tlsPort,
Handler: mux,
TLSConfig: &tls.Config{
Certificates: []tls.Certificate{cert},
},
}

log.Printf("TLS server listening on port %s\n", tlsPort)
log.Fatal(server.ListenAndServeTLS("", ""))
}()
}

// Block forever
select {}
}

// hello returns a http.HandlerFunc that uses the provided message pointer and printHeaders flag.
Expand Down Expand Up @@ -132,3 +178,39 @@ func getNodePtr() *string {
func getPrintHeadersSetting() bool {
return strings.ToLower(os.Getenv("PRINT_HTTP_REQUEST_HEADERS")) == "true"
}

// getTLSSetting checks the TLS environment variable.
func getTLSSetting() bool {
return strings.ToLower(os.Getenv("TLS")) == "true"
}

// generateSelfSignedCert generates a self-signed TLS certificate.
func generateSelfSignedCert() (tls.Certificate, error) {
priv, err := rsa.GenerateKey(rand.Reader, 4096)
if err != nil {
return tls.Certificate{}, err
}

template := x509.Certificate{
SerialNumber: big.NewInt(1),
Subject: pkix.Name{
Organization: []string{"echo Inc."},
},
NotBefore: time.Now(),
NotAfter: time.Now().Add(10 * 365 * 24 * time.Hour), // 10 years validity

KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
BasicConstraintsValid: true,
}

derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, &priv.PublicKey, priv)
if err != nil {
return tls.Certificate{}, err
}

certPEM := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
keyPEM := pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(priv)})

return tls.X509KeyPair(certPEM, keyPEM)
}

0 comments on commit 65d2ce8

Please sign in to comment.