forked from matrix-org/complement
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathconfig.go
185 lines (171 loc) · 5.36 KB
/
config.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
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
package config
import (
"bytes"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"crypto/x509/pkix"
"encoding/pem"
"fmt"
"math/big"
"os"
"strconv"
"strings"
"time"
)
type HostMount struct {
HostPath string
ContainerPath string
ReadOnly bool
}
type Complement struct {
BaseImageURI string
BaseImageArgs []string
DebugLoggingEnabled bool
AlwaysPrintServerLogs bool
BestEffort bool
EnvVarsPropagatePrefix string
SpawnHSTimeout time.Duration
KeepBlueprints []string
HostMounts []HostMount
// The namespace for all complement created blueprints and deployments
PackageNamespace string
// Certificate Authority generated values for this run of complement. Homeservers will use this
// as a base to derive their own signed Federation certificates.
CACertificate *x509.Certificate
CAPrivateKey *rsa.PrivateKey
}
func NewConfigFromEnvVars(pkgNamespace, baseImageURI string) *Complement {
cfg := &Complement{}
cfg.BaseImageURI = os.Getenv("COMPLEMENT_BASE_IMAGE")
if cfg.BaseImageURI == "" {
cfg.BaseImageURI = baseImageURI
}
cfg.BaseImageArgs = strings.Split(os.Getenv("COMPLEMENT_BASE_IMAGE_ARGS"), " ")
cfg.DebugLoggingEnabled = os.Getenv("COMPLEMENT_DEBUG") == "1"
cfg.AlwaysPrintServerLogs = os.Getenv("COMPLEMENT_ALWAYS_PRINT_SERVER_LOGS") == "1"
cfg.EnvVarsPropagatePrefix = os.Getenv("COMPLEMENT_SHARE_ENV_PREFIX")
cfg.SpawnHSTimeout = time.Duration(parseEnvWithDefault("COMPLEMENT_SPAWN_HS_TIMEOUT_SECS", 30)) * time.Second
if os.Getenv("COMPLEMENT_VERSION_CHECK_ITERATIONS") != "" {
fmt.Fprintln(os.Stderr, "Deprecated: COMPLEMENT_VERSION_CHECK_ITERATIONS will be removed in a later version. Use COMPLEMENT_SPAWN_HS_TIMEOUT_SECS instead which does the same thing and is clearer.")
// each iteration had a 50ms sleep between tries so the timeout is 50 * iteration ms
cfg.SpawnHSTimeout = time.Duration(50*parseEnvWithDefault("COMPLEMENT_VERSION_CHECK_ITERATIONS", 100)) * time.Millisecond
}
cfg.KeepBlueprints = strings.Split(os.Getenv("COMPLEMENT_KEEP_BLUEPRINTS"), " ")
var err error
hostMounts := os.Getenv("COMPLEMENT_HOST_MOUNTS")
if hostMounts != "" {
cfg.HostMounts, err = newHostMounts(strings.Split(hostMounts, ";"))
if err != nil {
panic("COMPLEMENT_HOST_MOUNTS parse error: " + err.Error())
}
}
if cfg.BaseImageURI == "" {
panic("COMPLEMENT_BASE_IMAGE must be set")
}
cfg.PackageNamespace = pkgNamespace
// create CA certs and keys
if err := cfg.GenerateCA(); err != nil {
panic("Failed to generate CA certificate/key: " + err.Error())
}
if cfg.PackageNamespace == "" {
panic("package namespace must be set")
}
return cfg
}
func (c *Complement) GenerateCA() error {
cert, key, err := generateCAValues()
if err != nil {
return err
}
c.CACertificate = cert
c.CAPrivateKey = key
return nil
}
func (c *Complement) CACertificateBytes() ([]byte, error) {
cert := bytes.NewBuffer(nil)
err := pem.Encode(cert, &pem.Block{Type: "CERTIFICATE", Bytes: c.CACertificate.Raw})
return cert.Bytes(), err
}
func (c *Complement) CAPrivateKeyBytes() ([]byte, error) {
caKey := bytes.NewBuffer(nil)
err := pem.Encode(caKey, &pem.Block{
Type: "RSA PRIVATE KEY",
Bytes: x509.MarshalPKCS1PrivateKey(c.CAPrivateKey),
})
return caKey.Bytes(), err
}
func parseEnvWithDefault(key string, def int) int {
s := os.Getenv(key)
if s != "" {
i, err := strconv.Atoi(s)
if err != nil {
// Don't bother trying to report it
return def
}
return i
}
return def
}
func newHostMounts(mounts []string) ([]HostMount, error) {
var hostMounts []HostMount
for _, m := range mounts {
segments := strings.Split(m, ":")
if len(segments) < 2 {
return nil, fmt.Errorf("mount '%s' malformed", m)
}
var ro string
if len(segments) == 3 {
ro = segments[2]
}
hostMounts = append(hostMounts, HostMount{
HostPath: segments[0],
ContainerPath: segments[1],
ReadOnly: ro == "ro" || ro == "readonly",
})
}
return hostMounts, nil
}
// Generate a certificate and private key
func generateCAValues() (*x509.Certificate, *rsa.PrivateKey, error) {
// valid for 10 years
certificateDuration := time.Hour * 24 * 365 * 10
priv, err := rsa.GenerateKey(rand.Reader, 4096)
if err != nil {
return nil, nil, err
}
notBefore := time.Now()
notAfter := notBefore.Add(certificateDuration)
serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
if err != nil {
return nil, nil, err
}
caCert := x509.Certificate{
SerialNumber: serialNumber,
NotBefore: notBefore,
NotAfter: notAfter,
IsCA: true,
KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageDigitalSignature | x509.KeyUsageCRLSign,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
BasicConstraintsValid: true,
Subject: pkix.Name{
Organization: []string{"matrix.org"},
Country: []string{"GB"},
Province: []string{"London"},
Locality: []string{"London"},
StreetAddress: []string{"123 Street"},
PostalCode: []string{"12345"},
CommonName: "Complement Test CA",
},
}
derBytes, err := x509.CreateCertificate(rand.Reader, &caCert, &caCert, &priv.PublicKey, priv)
if err != nil {
return nil, nil, err
}
selfSignedCert, err := x509.ParseCertificates(derBytes)
if err != nil {
return nil, nil, err
}
return selfSignedCert[0], priv, nil
}