This repository has been archived by the owner on Jan 14, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 1
/
entrypoint.go
165 lines (133 loc) · 4.11 KB
/
entrypoint.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
package main
import (
"encoding/json"
"fmt"
"github.com/flosch/pongo2/v5"
"github.com/pkg/errors"
"log"
"os"
"os/exec"
"strings"
)
const caddyFileSrc = "/usr/templates/Caddyfile.j2"
const caddyFilePath = "/etc/caddy/Caddyfile"
const wpFileSrc = "/usr/templates/wp-rules.conf.j2"
const wpFilePath = "/etc/caddy/rules/wordpress/rules.conf"
const crsFileSrc = "/usr/templates/crs-rules.conf.j2"
const crsFilePath = "/etc/caddy/rules/owasp-crs/configured.conf"
func main() {
usesOwnCaddyfile := getEnv("OWN_CADDYFILE", false).(bool)
usesWordpressRules := getEnv("ENABLE_RULE_WORDPRESS", false).(bool)
useCRSRules := getEnv("ENABLE_CRS", false).(bool)
debug := getEnv("DEBUG", false).(bool)
renderer := renderer{debug: debug}
if err := renderer.populateUpstreams(os.Environ()); err != nil {
log.Fatal(err)
}
renderer.buildPongoContext()
if usesWordpressRules {
if err := renderer.renderFile(wpFileSrc, wpFilePath); err != nil {
log.Fatalf("Cannot render Wordpress Ruleset: %v", err)
}
}
if useCRSRules {
if err := renderer.renderFile(crsFileSrc, crsFilePath); err != nil {
log.Fatalf("Cannot render CRS Ruleset: %v", err)
}
}
if !usesOwnCaddyfile {
if err := renderer.renderFile(caddyFileSrc, caddyFilePath); err != nil {
log.Fatalf("Cannot render Caddyfile: %v", err)
}
}
if len(os.Args) < 3 {
log.Fatalf("Need to provide at least 2 arguments e.g. /usr/bin/caddy run -pidfile /tmp/pid -config /etc/caddy/Caddyfile")
}
// run caddy server
caddy := exec.Command(os.Args[1], os.Args[2:]...)
caddy.Stdout = os.Stdout
caddy.Stdin = os.Stdin
caddy.Stderr = os.Stderr
if err := caddy.Run(); err != nil {
log.Fatalf("Process exited with error: %v", err)
}
}
type renderer struct {
upstreams []Upstream
ctx pongo2.Context
debug bool
}
func (r *renderer) renderFile(sourcePath string, targetPath string) error {
template := pongo2.Must(pongo2.FromFile(sourcePath))
out, err := template.Execute(r.ctx)
if err != nil {
return errors.Wrapf(err, "Cannot parse file '%v'", sourcePath)
}
r.printDebug(targetPath + ":")
r.printDebug(out)
if err := os.WriteFile(targetPath, []byte(out), os.FileMode(0755)); err != nil {
return errors.Wrapf(err, "Cannot write into file %v", targetPath)
}
return nil
}
func (r *renderer) buildPongoContext() {
ctx := pongo2.Context{}
// environment variable = regular variable
for _, env := range os.Environ() {
pair := strings.SplitN(env, "=", 2)
ctx[pair[0]] = getEnv(pair[0], "")
}
// all collected upstreams are internally objects
ctx["upstreams"] = r.upstreams
r.printDebug("Context:", ctx)
r.ctx = ctx
}
func (r *renderer) populateUpstreams(environ []string) error {
var upstreams []Upstream
// collect upstream configuration
for _, env := range environ {
pair := strings.SplitN(env, "=", 2)
r.printDebug("DEBUG: Checking environment variable if is upstream", env)
if strings.Contains(pair[0], "UPSTREAM_") {
if pair[1] == "" {
continue
}
upstream := &Upstream{}
r.printDebug("DEBUG: Creating upstream from ", env)
err := json.Unmarshal([]byte(pair[1]), upstream)
if err != nil {
return errors.Wrapf(err, "Cannot parse upstream configuration from: %v", env)
}
upstreams = append(r.upstreams, *upstream)
} else {
r.printDebug("DEBUG: not an upstream", env)
}
}
if len(upstreams) == 0 {
return errors.New("No upstream defined. Create environment variables e.g. UPSTREAM_1, UPSTREAM_2 and assign them a JSON with pass_to and hostname keys")
}
r.upstreams = upstreams
return nil
}
func (r *renderer) printDebug(text ...interface{}) {
if r.debug {
fmt.Println(text...)
}
}
func getEnv(name string, defaultValue interface{}) interface{} {
value, exists := os.LookupEnv(name)
if !exists {
return defaultValue
}
if value == "1" || value == "true" || value == "TRUE" || value == "yes" || value == "YES" || value == "Y" || value == "y" {
return true
}
if value == "0" || value == "false" || value == "FALSE" || value == "no" || value == "NO" || value == "N" || value == "n" {
return false
}
return value
}
type Upstream struct {
PassTo string `json:"pass_to"`
Hostname string `json:"hostname"`
}