configuration.go 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623
  1. package configuration
  2. import (
  3. "fmt"
  4. "io"
  5. "io/ioutil"
  6. "net/http"
  7. "reflect"
  8. "strings"
  9. "time"
  10. )
  11. // Configuration is a versioned registry configuration, intended to be provided by a yaml file, and
  12. // optionally modified by environment variables.
  13. //
  14. // Note that yaml field names should never include _ characters, since this is the separator used
  15. // in environment variable names.
  16. type Configuration struct {
  17. // Version is the version which defines the format of the rest of the configuration
  18. Version Version `yaml:"version"`
  19. // Log supports setting various parameters related to the logging
  20. // subsystem.
  21. Log struct {
  22. // Level is the granularity at which registry operations are logged.
  23. Level Loglevel `yaml:"level"`
  24. // Formatter overrides the default formatter with another. Options
  25. // include "text", "json" and "logstash".
  26. Formatter string `yaml:"formatter,omitempty"`
  27. // Fields allows users to specify static string fields to include in
  28. // the logger context.
  29. Fields map[string]interface{} `yaml:"fields,omitempty"`
  30. // Hooks allows users to configure the log hooks, to enabling the
  31. // sequent handling behavior, when defined levels of log message emit.
  32. Hooks []LogHook `yaml:"hooks,omitempty"`
  33. }
  34. // Loglevel is the level at which registry operations are logged. This is
  35. // deprecated. Please use Log.Level in the future.
  36. Loglevel Loglevel `yaml:"loglevel,omitempty"`
  37. // Storage is the configuration for the registry's storage driver
  38. Storage Storage `yaml:"storage"`
  39. // Auth allows configuration of various authorization methods that may be
  40. // used to gate requests.
  41. Auth Auth `yaml:"auth,omitempty"`
  42. // Middleware lists all middlewares to be used by the registry.
  43. Middleware map[string][]Middleware `yaml:"middleware,omitempty"`
  44. // Reporting is the configuration for error reporting
  45. Reporting Reporting `yaml:"reporting,omitempty"`
  46. // HTTP contains configuration parameters for the registry's http
  47. // interface.
  48. HTTP struct {
  49. // Addr specifies the bind address for the registry instance.
  50. Addr string `yaml:"addr,omitempty"`
  51. // Net specifies the net portion of the bind address. A default empty value means tcp.
  52. Net string `yaml:"net,omitempty"`
  53. // Host specifies an externally-reachable address for the registry, as a fully
  54. // qualified URL.
  55. Host string `yaml:"host,omitempty"`
  56. Prefix string `yaml:"prefix,omitempty"`
  57. // Secret specifies the secret key which HMAC tokens are created with.
  58. Secret string `yaml:"secret,omitempty"`
  59. // RelativeURLs specifies that relative URLs should be returned in
  60. // Location headers
  61. RelativeURLs bool `yaml:"relativeurls,omitempty"`
  62. // TLS instructs the http server to listen with a TLS configuration.
  63. // This only support simple tls configuration with a cert and key.
  64. // Mostly, this is useful for testing situations or simple deployments
  65. // that require tls. If more complex configurations are required, use
  66. // a proxy or make a proposal to add support here.
  67. TLS struct {
  68. // Certificate specifies the path to an x509 certificate file to
  69. // be used for TLS.
  70. Certificate string `yaml:"certificate,omitempty"`
  71. // Key specifies the path to the x509 key file, which should
  72. // contain the private portion for the file specified in
  73. // Certificate.
  74. Key string `yaml:"key,omitempty"`
  75. // Specifies the CA certs for client authentication
  76. // A file may contain multiple CA certificates encoded as PEM
  77. ClientCAs []string `yaml:"clientcas,omitempty"`
  78. // LetsEncrypt is used to configuration setting up TLS through
  79. // Let's Encrypt instead of manually specifying certificate and
  80. // key. If a TLS certificate is specified, the Let's Encrypt
  81. // section will not be used.
  82. LetsEncrypt struct {
  83. // CacheFile specifies cache file to use for lets encrypt
  84. // certificates and keys.
  85. CacheFile string `yaml:"cachefile,omitempty"`
  86. // Email is the email to use during Let's Encrypt registration
  87. Email string `yaml:"email,omitempty"`
  88. } `yaml:"letsencrypt,omitempty"`
  89. } `yaml:"tls,omitempty"`
  90. // Headers is a set of headers to include in HTTP responses. A common
  91. // use case for this would be security headers such as
  92. // Strict-Transport-Security. The map keys are the header names, and
  93. // the values are the associated header payloads.
  94. Headers http.Header `yaml:"headers,omitempty"`
  95. // Debug configures the http debug interface, if specified. This can
  96. // include services such as pprof, expvar and other data that should
  97. // not be exposed externally. Left disabled by default.
  98. Debug struct {
  99. // Addr specifies the bind address for the debug server.
  100. Addr string `yaml:"addr,omitempty"`
  101. } `yaml:"debug,omitempty"`
  102. // HTTP2 configuration options
  103. HTTP2 struct {
  104. // Specifies wether the registry should disallow clients attempting
  105. // to connect via http2. If set to true, only http/1.1 is supported.
  106. Disabled bool `yaml:"disabled,omitempty"`
  107. } `yaml:"http2,omitempty"`
  108. } `yaml:"http,omitempty"`
  109. // Notifications specifies configuration about various endpoint to which
  110. // registry events are dispatched.
  111. Notifications Notifications `yaml:"notifications,omitempty"`
  112. // Redis configures the redis pool available to the registry webapp.
  113. Redis struct {
  114. // Addr specifies the the redis instance available to the application.
  115. Addr string `yaml:"addr,omitempty"`
  116. // Password string to use when making a connection.
  117. Password string `yaml:"password,omitempty"`
  118. // DB specifies the database to connect to on the redis instance.
  119. DB int `yaml:"db,omitempty"`
  120. DialTimeout time.Duration `yaml:"dialtimeout,omitempty"` // timeout for connect
  121. ReadTimeout time.Duration `yaml:"readtimeout,omitempty"` // timeout for reads of data
  122. WriteTimeout time.Duration `yaml:"writetimeout,omitempty"` // timeout for writes of data
  123. // Pool configures the behavior of the redis connection pool.
  124. Pool struct {
  125. // MaxIdle sets the maximum number of idle connections.
  126. MaxIdle int `yaml:"maxidle,omitempty"`
  127. // MaxActive sets the maximum number of connections that should be
  128. // opened before blocking a connection request.
  129. MaxActive int `yaml:"maxactive,omitempty"`
  130. // IdleTimeout sets the amount time to wait before closing
  131. // inactive connections.
  132. IdleTimeout time.Duration `yaml:"idletimeout,omitempty"`
  133. } `yaml:"pool,omitempty"`
  134. } `yaml:"redis,omitempty"`
  135. Health Health `yaml:"health,omitempty"`
  136. Proxy Proxy `yaml:"proxy,omitempty"`
  137. // Compatibility is used for configurations of working with older or deprecated features.
  138. Compatibility struct {
  139. // Schema1 configures how schema1 manifests will be handled
  140. Schema1 struct {
  141. // TrustKey is the signing key to use for adding the signature to
  142. // schema1 manifests.
  143. TrustKey string `yaml:"signingkeyfile,omitempty"`
  144. } `yaml:"schema1,omitempty"`
  145. } `yaml:"compatibility,omitempty"`
  146. // Validation configures validation options for the registry.
  147. Validation struct {
  148. // Enabled enables the other options in this section.
  149. Enabled bool `yaml:"enabled,omitempty"`
  150. // Manifests configures manifest validation.
  151. Manifests struct {
  152. // URLs configures validation for URLs in pushed manifests.
  153. URLs struct {
  154. // Allow specifies regular expressions (https://godoc.org/regexp/syntax)
  155. // that URLs in pushed manifests must match.
  156. Allow []string `yaml:"allow,omitempty"`
  157. // Deny specifies regular expressions (https://godoc.org/regexp/syntax)
  158. // that URLs in pushed manifests must not match.
  159. Deny []string `yaml:"deny,omitempty"`
  160. } `yaml:"urls,omitempty"`
  161. } `yaml:"manifests,omitempty"`
  162. } `yaml:"validation,omitempty"`
  163. }
  164. // LogHook is composed of hook Level and Type.
  165. // After hooks configuration, it can execute the next handling automatically,
  166. // when defined levels of log message emitted.
  167. // Example: hook can sending an email notification when error log happens in app.
  168. type LogHook struct {
  169. // Disable lets user select to enable hook or not.
  170. Disabled bool `yaml:"disabled,omitempty"`
  171. // Type allows user to select which type of hook handler they want.
  172. Type string `yaml:"type,omitempty"`
  173. // Levels set which levels of log message will let hook executed.
  174. Levels []string `yaml:"levels,omitempty"`
  175. // MailOptions allows user to configurate email parameters.
  176. MailOptions MailOptions `yaml:"options,omitempty"`
  177. }
  178. // MailOptions provides the configuration sections to user, for specific handler.
  179. type MailOptions struct {
  180. SMTP struct {
  181. // Addr defines smtp host address
  182. Addr string `yaml:"addr,omitempty"`
  183. // Username defines user name to smtp host
  184. Username string `yaml:"username,omitempty"`
  185. // Password defines password of login user
  186. Password string `yaml:"password,omitempty"`
  187. // Insecure defines if smtp login skips the secure certification.
  188. Insecure bool `yaml:"insecure,omitempty"`
  189. } `yaml:"smtp,omitempty"`
  190. // From defines mail sending address
  191. From string `yaml:"from,omitempty"`
  192. // To defines mail receiving address
  193. To []string `yaml:"to,omitempty"`
  194. }
  195. // FileChecker is a type of entry in the health section for checking files.
  196. type FileChecker struct {
  197. // Interval is the duration in between checks
  198. Interval time.Duration `yaml:"interval,omitempty"`
  199. // File is the path to check
  200. File string `yaml:"file,omitempty"`
  201. // Threshold is the number of times a check must fail to trigger an
  202. // unhealthy state
  203. Threshold int `yaml:"threshold,omitempty"`
  204. }
  205. // HTTPChecker is a type of entry in the health section for checking HTTP URIs.
  206. type HTTPChecker struct {
  207. // Timeout is the duration to wait before timing out the HTTP request
  208. Timeout time.Duration `yaml:"timeout,omitempty"`
  209. // StatusCode is the expected status code
  210. StatusCode int
  211. // Interval is the duration in between checks
  212. Interval time.Duration `yaml:"interval,omitempty"`
  213. // URI is the HTTP URI to check
  214. URI string `yaml:"uri,omitempty"`
  215. // Headers lists static headers that should be added to all requests
  216. Headers http.Header `yaml:"headers"`
  217. // Threshold is the number of times a check must fail to trigger an
  218. // unhealthy state
  219. Threshold int `yaml:"threshold,omitempty"`
  220. }
  221. // TCPChecker is a type of entry in the health section for checking TCP servers.
  222. type TCPChecker struct {
  223. // Timeout is the duration to wait before timing out the TCP connection
  224. Timeout time.Duration `yaml:"timeout,omitempty"`
  225. // Interval is the duration in between checks
  226. Interval time.Duration `yaml:"interval,omitempty"`
  227. // Addr is the TCP address to check
  228. Addr string `yaml:"addr,omitempty"`
  229. // Threshold is the number of times a check must fail to trigger an
  230. // unhealthy state
  231. Threshold int `yaml:"threshold,omitempty"`
  232. }
  233. // Health provides the configuration section for health checks.
  234. type Health struct {
  235. // FileCheckers is a list of paths to check
  236. FileCheckers []FileChecker `yaml:"file,omitempty"`
  237. // HTTPCheckers is a list of URIs to check
  238. HTTPCheckers []HTTPChecker `yaml:"http,omitempty"`
  239. // TCPCheckers is a list of URIs to check
  240. TCPCheckers []TCPChecker `yaml:"tcp,omitempty"`
  241. // StorageDriver configures a health check on the configured storage
  242. // driver
  243. StorageDriver struct {
  244. // Enabled turns on the health check for the storage driver
  245. Enabled bool `yaml:"enabled,omitempty"`
  246. // Interval is the duration in between checks
  247. Interval time.Duration `yaml:"interval,omitempty"`
  248. // Threshold is the number of times a check must fail to trigger an
  249. // unhealthy state
  250. Threshold int `yaml:"threshold,omitempty"`
  251. } `yaml:"storagedriver,omitempty"`
  252. }
  253. // v0_1Configuration is a Version 0.1 Configuration struct
  254. // This is currently aliased to Configuration, as it is the current version
  255. type v0_1Configuration Configuration
  256. // UnmarshalYAML implements the yaml.Unmarshaler interface
  257. // Unmarshals a string of the form X.Y into a Version, validating that X and Y can represent uints
  258. func (version *Version) UnmarshalYAML(unmarshal func(interface{}) error) error {
  259. var versionString string
  260. err := unmarshal(&versionString)
  261. if err != nil {
  262. return err
  263. }
  264. newVersion := Version(versionString)
  265. if _, err := newVersion.major(); err != nil {
  266. return err
  267. }
  268. if _, err := newVersion.minor(); err != nil {
  269. return err
  270. }
  271. *version = newVersion
  272. return nil
  273. }
  274. // CurrentVersion is the most recent Version that can be parsed
  275. var CurrentVersion = MajorMinorVersion(0, 1)
  276. // Loglevel is the level at which operations are logged
  277. // This can be error, warn, info, or debug
  278. type Loglevel string
  279. // UnmarshalYAML implements the yaml.Umarshaler interface
  280. // Unmarshals a string into a Loglevel, lowercasing the string and validating that it represents a
  281. // valid loglevel
  282. func (loglevel *Loglevel) UnmarshalYAML(unmarshal func(interface{}) error) error {
  283. var loglevelString string
  284. err := unmarshal(&loglevelString)
  285. if err != nil {
  286. return err
  287. }
  288. loglevelString = strings.ToLower(loglevelString)
  289. switch loglevelString {
  290. case "error", "warn", "info", "debug":
  291. default:
  292. return fmt.Errorf("Invalid loglevel %s Must be one of [error, warn, info, debug]", loglevelString)
  293. }
  294. *loglevel = Loglevel(loglevelString)
  295. return nil
  296. }
  297. // Parameters defines a key-value parameters mapping
  298. type Parameters map[string]interface{}
  299. // Storage defines the configuration for registry object storage
  300. type Storage map[string]Parameters
  301. // Type returns the storage driver type, such as filesystem or s3
  302. func (storage Storage) Type() string {
  303. var storageType []string
  304. // Return only key in this map
  305. for k := range storage {
  306. switch k {
  307. case "maintenance":
  308. // allow configuration of maintenance
  309. case "cache":
  310. // allow configuration of caching
  311. case "delete":
  312. // allow configuration of delete
  313. case "redirect":
  314. // allow configuration of redirect
  315. default:
  316. storageType = append(storageType, k)
  317. }
  318. }
  319. if len(storageType) > 1 {
  320. panic("multiple storage drivers specified in configuration or environment: " + strings.Join(storageType, ", "))
  321. }
  322. if len(storageType) == 1 {
  323. return storageType[0]
  324. }
  325. return ""
  326. }
  327. // Parameters returns the Parameters map for a Storage configuration
  328. func (storage Storage) Parameters() Parameters {
  329. return storage[storage.Type()]
  330. }
  331. // setParameter changes the parameter at the provided key to the new value
  332. func (storage Storage) setParameter(key string, value interface{}) {
  333. storage[storage.Type()][key] = value
  334. }
  335. // UnmarshalYAML implements the yaml.Unmarshaler interface
  336. // Unmarshals a single item map into a Storage or a string into a Storage type with no parameters
  337. func (storage *Storage) UnmarshalYAML(unmarshal func(interface{}) error) error {
  338. var storageMap map[string]Parameters
  339. err := unmarshal(&storageMap)
  340. if err == nil {
  341. if len(storageMap) > 1 {
  342. types := make([]string, 0, len(storageMap))
  343. for k := range storageMap {
  344. switch k {
  345. case "maintenance":
  346. // allow for configuration of maintenance
  347. case "cache":
  348. // allow configuration of caching
  349. case "delete":
  350. // allow configuration of delete
  351. case "redirect":
  352. // allow configuration of redirect
  353. default:
  354. types = append(types, k)
  355. }
  356. }
  357. if len(types) > 1 {
  358. return fmt.Errorf("Must provide exactly one storage type. Provided: %v", types)
  359. }
  360. }
  361. *storage = storageMap
  362. return nil
  363. }
  364. var storageType string
  365. err = unmarshal(&storageType)
  366. if err == nil {
  367. *storage = Storage{storageType: Parameters{}}
  368. return nil
  369. }
  370. return err
  371. }
  372. // MarshalYAML implements the yaml.Marshaler interface
  373. func (storage Storage) MarshalYAML() (interface{}, error) {
  374. if storage.Parameters() == nil {
  375. return storage.Type(), nil
  376. }
  377. return map[string]Parameters(storage), nil
  378. }
  379. // Auth defines the configuration for registry authorization.
  380. type Auth map[string]Parameters
  381. // Type returns the auth type, such as htpasswd or token
  382. func (auth Auth) Type() string {
  383. // Return only key in this map
  384. for k := range auth {
  385. return k
  386. }
  387. return ""
  388. }
  389. // Parameters returns the Parameters map for an Auth configuration
  390. func (auth Auth) Parameters() Parameters {
  391. return auth[auth.Type()]
  392. }
  393. // setParameter changes the parameter at the provided key to the new value
  394. func (auth Auth) setParameter(key string, value interface{}) {
  395. auth[auth.Type()][key] = value
  396. }
  397. // UnmarshalYAML implements the yaml.Unmarshaler interface
  398. // Unmarshals a single item map into a Storage or a string into a Storage type with no parameters
  399. func (auth *Auth) UnmarshalYAML(unmarshal func(interface{}) error) error {
  400. var m map[string]Parameters
  401. err := unmarshal(&m)
  402. if err == nil {
  403. if len(m) > 1 {
  404. types := make([]string, 0, len(m))
  405. for k := range m {
  406. types = append(types, k)
  407. }
  408. // TODO(stevvooe): May want to change this slightly for
  409. // authorization to allow multiple challenges.
  410. return fmt.Errorf("must provide exactly one type. Provided: %v", types)
  411. }
  412. *auth = m
  413. return nil
  414. }
  415. var authType string
  416. err = unmarshal(&authType)
  417. if err == nil {
  418. *auth = Auth{authType: Parameters{}}
  419. return nil
  420. }
  421. return err
  422. }
  423. // MarshalYAML implements the yaml.Marshaler interface
  424. func (auth Auth) MarshalYAML() (interface{}, error) {
  425. if auth.Parameters() == nil {
  426. return auth.Type(), nil
  427. }
  428. return map[string]Parameters(auth), nil
  429. }
  430. // Notifications configures multiple http endpoints.
  431. type Notifications struct {
  432. // Endpoints is a list of http configurations for endpoints that
  433. // respond to webhook notifications. In the future, we may allow other
  434. // kinds of endpoints, such as external queues.
  435. Endpoints []Endpoint `yaml:"endpoints,omitempty"`
  436. }
  437. // Endpoint describes the configuration of an http webhook notification
  438. // endpoint.
  439. type Endpoint struct {
  440. Name string `yaml:"name"` // identifies the endpoint in the registry instance.
  441. Disabled bool `yaml:"disabled"` // disables the endpoint
  442. URL string `yaml:"url"` // post url for the endpoint.
  443. Headers http.Header `yaml:"headers"` // static headers that should be added to all requests
  444. Timeout time.Duration `yaml:"timeout"` // HTTP timeout
  445. Threshold int `yaml:"threshold"` // circuit breaker threshold before backing off on failure
  446. Backoff time.Duration `yaml:"backoff"` // backoff duration
  447. }
  448. // Reporting defines error reporting methods.
  449. type Reporting struct {
  450. // Bugsnag configures error reporting for Bugsnag (bugsnag.com).
  451. Bugsnag BugsnagReporting `yaml:"bugsnag,omitempty"`
  452. // NewRelic configures error reporting for NewRelic (newrelic.com)
  453. NewRelic NewRelicReporting `yaml:"newrelic,omitempty"`
  454. }
  455. // BugsnagReporting configures error reporting for Bugsnag (bugsnag.com).
  456. type BugsnagReporting struct {
  457. // APIKey is the Bugsnag api key.
  458. APIKey string `yaml:"apikey,omitempty"`
  459. // ReleaseStage tracks where the registry is deployed.
  460. // Examples: production, staging, development
  461. ReleaseStage string `yaml:"releasestage,omitempty"`
  462. // Endpoint is used for specifying an enterprise Bugsnag endpoint.
  463. Endpoint string `yaml:"endpoint,omitempty"`
  464. }
  465. // NewRelicReporting configures error reporting for NewRelic (newrelic.com)
  466. type NewRelicReporting struct {
  467. // LicenseKey is the NewRelic user license key
  468. LicenseKey string `yaml:"licensekey,omitempty"`
  469. // Name is the component name of the registry in NewRelic
  470. Name string `yaml:"name,omitempty"`
  471. // Verbose configures debug output to STDOUT
  472. Verbose bool `yaml:"verbose,omitempty"`
  473. }
  474. // Middleware configures named middlewares to be applied at injection points.
  475. type Middleware struct {
  476. // Name the middleware registers itself as
  477. Name string `yaml:"name"`
  478. // Flag to disable middleware easily
  479. Disabled bool `yaml:"disabled,omitempty"`
  480. // Map of parameters that will be passed to the middleware's initialization function
  481. Options Parameters `yaml:"options"`
  482. }
  483. // Proxy configures the registry as a pull through cache
  484. type Proxy struct {
  485. // RemoteURL is the URL of the remote registry
  486. RemoteURL string `yaml:"remoteurl"`
  487. // Username of the hub user
  488. Username string `yaml:"username"`
  489. // Password of the hub user
  490. Password string `yaml:"password"`
  491. }
  492. // Parse parses an input configuration yaml document into a Configuration struct
  493. // This should generally be capable of handling old configuration format versions
  494. //
  495. // Environment variables may be used to override configuration parameters other than version,
  496. // following the scheme below:
  497. // Configuration.Abc may be replaced by the value of REGISTRY_ABC,
  498. // Configuration.Abc.Xyz may be replaced by the value of REGISTRY_ABC_XYZ, and so forth
  499. func Parse(rd io.Reader) (*Configuration, error) {
  500. in, err := ioutil.ReadAll(rd)
  501. if err != nil {
  502. return nil, err
  503. }
  504. p := NewParser("registry", []VersionedParseInfo{
  505. {
  506. Version: MajorMinorVersion(0, 1),
  507. ParseAs: reflect.TypeOf(v0_1Configuration{}),
  508. ConversionFunc: func(c interface{}) (interface{}, error) {
  509. if v0_1, ok := c.(*v0_1Configuration); ok {
  510. if v0_1.Loglevel == Loglevel("") {
  511. v0_1.Loglevel = Loglevel("info")
  512. }
  513. if v0_1.Storage.Type() == "" {
  514. return nil, fmt.Errorf("No storage configuration provided")
  515. }
  516. return (*Configuration)(v0_1), nil
  517. }
  518. return nil, fmt.Errorf("Expected *v0_1Configuration, received %#v", c)
  519. },
  520. },
  521. })
  522. config := new(Configuration)
  523. err = p.Parse(in, config)
  524. if err != nil {
  525. return nil, err
  526. }
  527. return config, nil
  528. }