2018-06-12 10:09:47 +00:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"crypto/sha256"
|
|
|
|
"encoding/hex"
|
2018-12-10 10:59:24 +00:00
|
|
|
"io"
|
2018-06-12 10:09:47 +00:00
|
|
|
"log"
|
2018-12-10 10:59:24 +00:00
|
|
|
"net/http"
|
2020-03-04 23:06:49 +00:00
|
|
|
"net/url"
|
2018-06-12 10:09:47 +00:00
|
|
|
"path/filepath"
|
2018-12-10 13:44:05 +00:00
|
|
|
"text/template"
|
2018-06-12 10:09:47 +00:00
|
|
|
|
2018-06-16 12:04:58 +00:00
|
|
|
cfsslconfig "github.com/cloudflare/cfssl/config"
|
2020-03-05 09:55:59 +00:00
|
|
|
restful "github.com/emicklei/go-restful"
|
2018-06-12 10:09:47 +00:00
|
|
|
yaml "gopkg.in/yaml.v2"
|
2018-06-16 12:04:58 +00:00
|
|
|
|
2018-06-12 10:09:47 +00:00
|
|
|
"novit.nc/direktil/pkg/config"
|
2018-12-10 10:59:24 +00:00
|
|
|
"novit.nc/direktil/pkg/localconfig"
|
2018-06-12 10:09:47 +00:00
|
|
|
)
|
|
|
|
|
2020-03-05 09:55:59 +00:00
|
|
|
var cmdlineParam = restful.QueryParameter("cmdline", "Linux kernel cmdline addition")
|
|
|
|
|
2018-06-12 10:09:47 +00:00
|
|
|
type renderContext struct {
|
2018-12-10 10:59:24 +00:00
|
|
|
Host *localconfig.Host
|
|
|
|
SSLConfig string
|
2020-03-04 23:06:49 +00:00
|
|
|
|
|
|
|
// Linux kernel extra cmdline
|
|
|
|
CmdLine string `yaml:"-"`
|
2018-06-12 10:09:47 +00:00
|
|
|
}
|
|
|
|
|
2018-12-10 10:59:24 +00:00
|
|
|
func renderCtx(w http.ResponseWriter, r *http.Request, ctx *renderContext, what string,
|
|
|
|
create func(out io.Writer, ctx *renderContext) error) error {
|
2018-07-03 07:35:52 +00:00
|
|
|
|
2018-12-10 10:59:24 +00:00
|
|
|
tag, err := ctx.Tag()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
2018-07-03 07:35:52 +00:00
|
|
|
}
|
2018-06-12 10:09:47 +00:00
|
|
|
|
2020-03-05 09:55:59 +00:00
|
|
|
ctx.CmdLine = r.URL.Query().Get(cmdlineParam.Data().Name)
|
2020-03-04 23:06:49 +00:00
|
|
|
|
|
|
|
if ctx.CmdLine != "" {
|
|
|
|
what = what + "?cmdline=" + url.QueryEscape(ctx.CmdLine)
|
|
|
|
}
|
|
|
|
|
2018-12-10 10:59:24 +00:00
|
|
|
// get it or create it
|
|
|
|
content, meta, err := casStore.GetOrCreate(tag, what, func(out io.Writer) error {
|
|
|
|
log.Printf("building %s for %q", what, ctx.Host.Name)
|
|
|
|
return create(out, ctx)
|
|
|
|
})
|
2018-06-12 10:09:47 +00:00
|
|
|
|
2018-12-10 10:59:24 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
2018-06-12 10:09:47 +00:00
|
|
|
}
|
|
|
|
|
2018-12-10 10:59:24 +00:00
|
|
|
// serve it
|
2020-03-04 23:06:49 +00:00
|
|
|
log.Printf("sending %s for %q", what, ctx.Host.Name)
|
2018-12-10 10:59:24 +00:00
|
|
|
http.ServeContent(w, r, what, meta.ModTime(), content)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2019-01-21 22:44:11 +00:00
|
|
|
var prevSSLConfig = "-"
|
|
|
|
|
2018-12-10 10:59:24 +00:00
|
|
|
func newRenderContext(host *localconfig.Host, cfg *localconfig.Config) (ctx *renderContext, err error) {
|
2019-01-21 22:44:11 +00:00
|
|
|
if prevSSLConfig != cfg.SSLConfig {
|
|
|
|
var sslCfg *cfsslconfig.Config
|
|
|
|
|
|
|
|
if len(cfg.SSLConfig) == 0 {
|
|
|
|
sslCfg = &cfsslconfig.Config{}
|
|
|
|
} else {
|
|
|
|
sslCfg, err = cfsslconfig.LoadConfig([]byte(cfg.SSLConfig))
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
err = loadSecretData(sslCfg)
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
prevSSLConfig = cfg.SSLConfig
|
|
|
|
}
|
|
|
|
|
2018-06-12 10:09:47 +00:00
|
|
|
return &renderContext{
|
2018-12-10 10:59:24 +00:00
|
|
|
SSLConfig: cfg.SSLConfig,
|
|
|
|
Host: host,
|
2018-07-03 07:35:52 +00:00
|
|
|
}, nil
|
2018-06-12 10:09:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (ctx *renderContext) Config() (ba []byte, cfg *config.Config, err error) {
|
2018-12-10 10:59:24 +00:00
|
|
|
tmpl, err := template.New(ctx.Host.Name + "/config").
|
2019-12-26 10:10:23 +00:00
|
|
|
Funcs(templateFuncs).
|
2018-12-10 10:59:24 +00:00
|
|
|
Parse(ctx.Host.Config)
|
2018-07-07 01:22:35 +00:00
|
|
|
|
2018-12-10 10:59:24 +00:00
|
|
|
if err != nil {
|
2018-07-07 01:22:35 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2018-06-20 03:01:10 +00:00
|
|
|
buf := bytes.NewBuffer(make([]byte, 0, 4096))
|
2018-12-10 10:59:24 +00:00
|
|
|
if err = tmpl.Execute(buf, nil); err != nil {
|
2018-06-20 03:01:10 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if secretData.Changed() {
|
|
|
|
err = secretData.Save()
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ba = buf.Bytes()
|
|
|
|
|
|
|
|
cfg = &config.Config{}
|
|
|
|
|
|
|
|
if err = yaml.Unmarshal(buf.Bytes(), cfg); err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2018-06-12 10:09:47 +00:00
|
|
|
func (ctx *renderContext) distFilePath(path ...string) string {
|
|
|
|
return filepath.Join(append([]string{*dataDir, "dist"}, path...)...)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (ctx *renderContext) Tag() (string, error) {
|
|
|
|
h := sha256.New()
|
|
|
|
|
|
|
|
_, cfg, err := ctx.Config()
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
|
|
|
enc := yaml.NewEncoder(h)
|
|
|
|
|
|
|
|
for _, o := range []interface{}{cfg, ctx} {
|
|
|
|
if err := enc.Encode(o); err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return hex.EncodeToString(h.Sum(nil)), nil
|
|
|
|
}
|
2018-06-13 03:12:12 +00:00
|
|
|
|
2018-06-20 03:01:10 +00:00
|
|
|
func asMap(v interface{}) map[string]interface{} {
|
|
|
|
ba, err := yaml.Marshal(v)
|
2018-06-13 03:12:12 +00:00
|
|
|
if err != nil {
|
|
|
|
panic(err) // shouldn't happen
|
|
|
|
}
|
|
|
|
|
|
|
|
result := make(map[string]interface{})
|
|
|
|
|
|
|
|
if err := yaml.Unmarshal(ba, result); err != nil {
|
2018-06-20 03:01:10 +00:00
|
|
|
panic(err) // shouldn't happen
|
2018-06-13 03:12:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return result
|
|
|
|
}
|