main
  1//go:build mage
  2// +build mage
  3
  4package main
  5
  6import (
  7	"context"
  8	"encoding/json"
  9	"io/ioutil"
 10	"os"
 11	"path/filepath"
 12	"strings"
 13
 14	"github.com/magefile/mage/mg"
 15	"github.com/magefile/mage/sh"
 16	"github.com/magefile/mage/target"
 17	"github.com/xlgmokha/x/pkg/env"
 18	"github.com/xlgmokha/x/pkg/x"
 19)
 20
 21type Step mg.Namespace
 22
 23func (s Step) Clean() error {
 24	globs := []string{
 25		"tmp/step/*/*",
 26	}
 27	for _, item := range globs {
 28		fs, err := filepath.Glob(item)
 29		if err != nil {
 30			return err
 31		}
 32		for _, f := range fs {
 33			if strings.HasSuffix(f, "/.keep") {
 34				continue
 35			}
 36			if err := os.RemoveAll(f); err != nil {
 37				return err
 38			}
 39		}
 40	}
 41	return nil
 42}
 43
 44func (s Step) Setup() {
 45	mg.SerialDeps(s.mkPassword, s.createCA, s.enableACMEProvisioner)
 46}
 47
 48func (s Step) Install() error {
 49	return sh.RunWithV(
 50		s.env(),
 51		"step",
 52		"certificate",
 53		"install",
 54		s.pathPlus("/certs/root_ca.crt"),
 55	)
 56}
 57
 58func (s Step) Server(ctx context.Context) error {
 59	mg.SerialDeps(s.Setup)
 60
 61	return sh.RunWithV(
 62		s.env(),
 63		"step-ca",
 64		s.pathPlus("config/ca.json"),
 65		"--password-file="+s.pathPlus("password.txt"),
 66	)
 67}
 68
 69func (s Step) Provisioners() error {
 70	return sh.RunV("curl", "-k", "-s", "https://localhost:8081/provisioners")
 71}
 72
 73func (s Step) ACME() error {
 74	return sh.RunV("curl", "-k", "-s", "https://localhost:8081/acme/acme/directory")
 75}
 76
 77func (s Step) Status() {
 78	mg.SerialDeps(s.Provisioners, s.ACME)
 79}
 80
 81func (s Step) mkPassword() error {
 82	file := s.passwordFile()
 83	if ok, err := target.Dir(file); err != nil || !ok {
 84		return nil
 85	}
 86
 87	return os.WriteFile(file, []byte("password"), 0600)
 88}
 89
 90func (s Step) createCA() error {
 91	if ok, err := target.Dir(s.pathPlus("config/ca.json"), s.passwordFile()); err != nil || !ok {
 92		return nil
 93	}
 94
 95	return sh.RunWithV(
 96		s.env(),
 97		"step",
 98		"ca",
 99		"init",
100		"--deployment-type=standalone",
101		"--address=localhost:8081",
102		"--dns=localhost",
103		"--dns=*.localhost",
104		"--name=CA",
105		"--provisioner=example",
106		"--provisioner-password-file="+s.passwordFile(),
107		"--password-file="+s.passwordFile(),
108	)
109}
110
111func (s Step) enableACMEProvisioner() error {
112	bytes, err := ioutil.ReadFile(s.pathPlus("config/ca.json"))
113	if err != nil {
114		return err
115	}
116
117	items := map[string]interface{}{}
118	if err := json.Unmarshal(bytes, &items); err != nil {
119		return err
120	}
121
122	provisioners := items["authority"].(map[string]interface{})["provisioners"].([]interface{})
123	if len(provisioners) < 2 {
124		return sh.RunWithV(s.env(), "step", "ca", "provisioner", "add", "acme", "--type", "ACME")
125	}
126	return nil
127}
128
129func (step Step) passwordFile() string {
130	return step.pathPlus("password.txt")
131}
132
133func (s Step) path() string {
134	return env.Fetch("STEPPATH", filepath.Join(x.Must(os.Getwd()), "/tmp/step"))
135}
136
137func (s Step) env() map[string]string {
138	return map[string]string{
139		"STEPPATH": s.path(),
140		"HOST":     "localhost",
141		"PORT":     "8081",
142	}
143}
144
145func (s Step) pathPlus(path string) string {
146	return filepath.Join(s.path(), path)
147}