@marcel91200 / caddy Mirror · read-only
Fork 0

Fast and extensible multi-platform HTTP/1-2-3 web server with automatic HTTPS

Mirrored from https://github.com/Slazee/caddy.git

Synced Nov 10, 23:45 UTC

master
go 230 lines 6.07 KB
Raw
1 // Copyright 2015 Matthew Holt and The Caddy Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 package caddycmd
16
17 import (
18 "archive/tar"
19 "context"
20 "encoding/json"
21 "errors"
22 "fmt"
23 "io"
24 "io/fs"
25 "os"
26
27 "github.com/caddyserver/certmagic"
28
29 "github.com/caddyserver/caddy/v2"
30 )
31
32 type storVal struct {
33 StorageRaw json.RawMessage `json:"storage,omitempty" caddy:"namespace=caddy.storage inline_key=module"`
34 }
35
36 // determineStorage returns the top-level storage module from the given config.
37 // It may return nil even if no error.
38 func determineStorage(configFile string, configAdapter string) (*storVal, error) {
39 cfg, _, _, err := LoadConfig(configFile, configAdapter)
40 if err != nil {
41 return nil, err
42 }
43
44 // storage defaults to FileStorage if not explicitly
45 // defined in the config, so the config can be valid
46 // json but unmarshaling will fail.
47 if !json.Valid(cfg) {
48 return nil, &json.SyntaxError{}
49 }
50 var tmpStruct storVal
51 err = json.Unmarshal(cfg, &tmpStruct)
52 if err != nil {
53 // default case, ignore the error
54 if _, ok := errors.AsType[*json.SyntaxError](err); ok {
55 return nil, nil
56 }
57 return nil, err
58 }
59
60 return &tmpStruct, nil
61 }
62
63 func cmdImportStorage(fl Flags) (int, error) {
64 importStorageCmdConfigFlag := fl.String("config")
65 importStorageCmdImportFile := fl.String("input")
66
67 if importStorageCmdConfigFlag == "" {
68 return caddy.ExitCodeFailedStartup, errors.New("--config is required")
69 }
70 if importStorageCmdImportFile == "" {
71 return caddy.ExitCodeFailedStartup, errors.New("--input is required")
72 }
73
74 // extract storage from config if possible
75 storageCfg, err := determineStorage(importStorageCmdConfigFlag, "")
76 if err != nil {
77 return caddy.ExitCodeFailedStartup, err
78 }
79
80 // load specified storage or fallback to default
81 var stor certmagic.Storage
82 ctx, cancel := caddy.NewContext(caddy.Context{Context: context.Background()})
83 defer cancel()
84 if storageCfg != nil && storageCfg.StorageRaw != nil {
85 val, err := ctx.LoadModule(storageCfg, "StorageRaw")
86 if err != nil {
87 return caddy.ExitCodeFailedStartup, err
88 }
89 stor, err = val.(caddy.StorageConverter).CertMagicStorage()
90 if err != nil {
91 return caddy.ExitCodeFailedStartup, err
92 }
93 } else {
94 stor = caddy.DefaultStorage
95 }
96
97 // setup input
98 var f *os.File
99 if importStorageCmdImportFile == "-" {
100 f = os.Stdin
101 } else {
102 f, err = os.Open(importStorageCmdImportFile)
103 if err != nil {
104 return caddy.ExitCodeFailedStartup, fmt.Errorf("opening input file: %v", err)
105 }
106 defer f.Close()
107 }
108
109 // store each archive element
110 tr := tar.NewReader(f)
111 for {
112 hdr, err := tr.Next()
113 if err == io.EOF {
114 break
115 }
116 if err != nil {
117 return caddy.ExitCodeFailedQuit, fmt.Errorf("reading archive: %v", err)
118 }
119
120 b, err := io.ReadAll(tr)
121 if err != nil {
122 return caddy.ExitCodeFailedQuit, fmt.Errorf("reading archive: %v", err)
123 }
124
125 err = stor.Store(ctx, hdr.Name, b)
126 if err != nil {
127 return caddy.ExitCodeFailedQuit, fmt.Errorf("reading archive: %v", err)
128 }
129 }
130
131 fmt.Println("Successfully imported storage")
132 return caddy.ExitCodeSuccess, nil
133 }
134
135 func cmdExportStorage(fl Flags) (int, error) {
136 exportStorageCmdConfigFlag := fl.String("config")
137 exportStorageCmdOutputFlag := fl.String("output")
138
139 if exportStorageCmdConfigFlag == "" {
140 return caddy.ExitCodeFailedStartup, errors.New("--config is required")
141 }
142 if exportStorageCmdOutputFlag == "" {
143 return caddy.ExitCodeFailedStartup, errors.New("--output is required")
144 }
145
146 // extract storage from config if possible
147 storageCfg, err := determineStorage(exportStorageCmdConfigFlag, "")
148 if err != nil {
149 return caddy.ExitCodeFailedStartup, err
150 }
151
152 // load specified storage or fallback to default
153 var stor certmagic.Storage
154 ctx, cancel := caddy.NewContext(caddy.Context{Context: context.Background()})
155 defer cancel()
156 if storageCfg != nil && storageCfg.StorageRaw != nil {
157 val, err := ctx.LoadModule(storageCfg, "StorageRaw")
158 if err != nil {
159 return caddy.ExitCodeFailedStartup, err
160 }
161 stor, err = val.(caddy.StorageConverter).CertMagicStorage()
162 if err != nil {
163 return caddy.ExitCodeFailedStartup, err
164 }
165 } else {
166 stor = caddy.DefaultStorage
167 }
168
169 // enumerate all keys
170 keys, err := stor.List(ctx, "", true)
171 if err != nil {
172 return caddy.ExitCodeFailedStartup, err
173 }
174
175 // setup output
176 var f *os.File
177 if exportStorageCmdOutputFlag == "-" {
178 f = os.Stdout
179 } else {
180 f, err = os.Create(exportStorageCmdOutputFlag)
181 if err != nil {
182 return caddy.ExitCodeFailedStartup, fmt.Errorf("opening output file: %v", err)
183 }
184 defer f.Close()
185 }
186
187 // `IsTerminal: true` keys hold the values we
188 // care about, write them out
189 tw := tar.NewWriter(f)
190 for _, k := range keys {
191 info, err := stor.Stat(ctx, k)
192 if err != nil {
193 if errors.Is(err, fs.ErrNotExist) {
194 caddy.Log().Warn(fmt.Sprintf("key: %s removed while export is in-progress", k))
195 continue
196 }
197 return caddy.ExitCodeFailedQuit, err
198 }
199
200 if info.IsTerminal {
201 v, err := stor.Load(ctx, k)
202 if err != nil {
203 if errors.Is(err, fs.ErrNotExist) {
204 caddy.Log().Warn(fmt.Sprintf("key: %s removed while export is in-progress", k))
205 continue
206 }
207 return caddy.ExitCodeFailedQuit, err
208 }
209
210 hdr := &tar.Header{
211 Name: k,
212 Mode: 0o600,
213 Size: int64(len(v)),
214 ModTime: info.Modified,
215 }
216
217 if err = tw.WriteHeader(hdr); err != nil {
218 return caddy.ExitCodeFailedQuit, fmt.Errorf("writing archive: %v", err)
219 }
220 if _, err = tw.Write(v); err != nil {
221 return caddy.ExitCodeFailedQuit, fmt.Errorf("writing archive: %v", err)
222 }
223 }
224 }
225 if err = tw.Close(); err != nil {
226 return caddy.ExitCodeFailedQuit, fmt.Errorf("writing archive: %v", err)
227 }
228
229 return caddy.ExitCodeSuccess, nil
230 }