refactor: certs (#4912)

This refactors the suites to use a Enterprise Root CA PKI signed certificate so the CA public certificate can be trusted. This is particularly useful for webauthn in Chrome.
pull/4913/head
James Elliott 2023-02-11 14:11:40 +11:00 committed by GitHub
parent f1c1a83ee0
commit 8e4b660f15
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
85 changed files with 876 additions and 424 deletions

View File

@ -108,8 +108,9 @@ func genCLIDocWriteIndex(path, name string) (err error) {
func prepend(input string) string {
now := time.Now()
pathz := strings.Split(strings.Replace(input, ".md", "", 1), "\\")
parts := strings.Split(pathz[len(pathz)-1], "_")
_, filename := filepath.Split(strings.Replace(input, ".md", "", 1))
parts := strings.Split(filename, "_")
cmd := parts[0]

View File

@ -64,7 +64,7 @@ func cmdBootstrapRun(_ *cobra.Command, _ []string) {
fmt.Println()
bootstrapPrintln("Run 'authelia-scripts suites setup Standalone' to start Authelia and visit https://home.example.com:8080.")
bootstrapPrintln("More details at https://github.com/authelia/authelia/blob/master/docs/getting-started.md")
bootstrapPrintln("More details at https://www.authelia.com/contributing/development/build-and-test/")
}
var hostEntries = []HostEntry{

View File

@ -35,10 +35,18 @@ The way this format works is you can either configure an integer or a string in
supply an integer, it is considered a representation of seconds. If you supply a string, it parses the string in blocks
of quantities and units (number followed by a unit letter). For example `5h` indicates a quantity of 5 units of `h`.
The following is ignored:
- all spaces
- leading zeros
While you can use multiple of these blocks in combination, we suggest keeping it simple and use a single value.
### Unit Legend
#### Short Units
These values have been available for a long time.
| Unit | Associated Letter |
|:-------:|:-----------------:|
| Years | y |
@ -49,6 +57,21 @@ While you can use multiple of these blocks in combination, we suggest keeping it
| Minutes | m |
| Seconds | s |
#### Long Units
These values are more human readable but have only been available since v4.38.0.
| Unit | Human Readable Long Unit |
|:------------:|:-----------------------------:|
| Years | `year`, `years` |
| Months | `month`, `months` |
| Weeks | `week`, `weeks` |
| Days | `day`, `days` |
| Hours | `hour`, `hours` |
| Minutes | `minute`, `minutes` |
| Seconds | `second`, `seconds` |
| Milliseconds | `millisecond`, `milliseconds` |
### Examples
| Desired Value | Configuration Examples |

View File

@ -168,6 +168,13 @@ static_resources:
- name: envoy.filters.http.ext_authz
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthz
transport_api_version: v3
allowed_headers:
patterns:
- exact: authorization
- exact: proxy-authorization
- exact: accept
- exact: cookie
http_service:
path_prefix: /api/authz/ext-authz/
server_uri:
@ -208,9 +215,9 @@ static_resources:
clusters:
- name: nextcloud
connect_timeout: 0.25s
type: LOGICAL_DNS
dns_lookup_family: V4_ONLY
lb_policy: ROUND_ROBIN
type: logical_dns
dns_lookup_family: v4_only
lb_policy: round_robin
load_assignment:
cluster_name: nextcloud
endpoints:
@ -222,9 +229,9 @@ static_resources:
port_value: 80
- name: authelia
connect_timeout: 0.25s
type: LOGICAL_DNS
dns_lookup_family: V4_ONLY
lb_policy: ROUND_ROBIN
type: logical_dns
dns_lookup_family: v4_only
lb_policy: round_robin
load_assignment:
cluster_name: authelia
endpoints:
@ -234,6 +241,17 @@ static_resources:
socket_address:
address: authelia
port_value: 9091
layered_runtime:
layers:
- name: static_layer_0
static_layer:
envoy:
resource_limits:
listener:
example_listener_name:
connection_limit: 10000
overload:
global_downstream_max_connections: 50000
```
{{< /details >}}

View File

@ -35,20 +35,23 @@ authelia crypto certificate ecdsa generate --help
### Options
```
--bundle enables generating the certificate bundle if the --path.ca flag is set
--ca create the certificate as a certificate authority certificate
-n, --common-name string certificate common name
--country strings certificate country
-b, --curve string Sets the elliptic curve which can be P224, P256, P384, or P521 (default "P256")
-d, --directory string directory where the generated keys, certificates, etc will be stored
--duration duration duration of time the certificate is valid for (default 8760h0m0s)
--duration string duration of time the certificate is valid for (default "1y")
--extended-usage strings specify the extended usage types of the certificate
--file.ca-certificate string certificate authority certificate to use when signing this certificate (default "ca.public.crt")
--file.ca-private-key string certificate authority private key to use to signing this certificate (default "ca.private.pem")
--file.certificate string name of the file to export the certificate data to (default "public.crt")
--file.certificate-bundle string name of the file to export the certificate bundle data to when the --bundle flag is set (default "public.bundle.crt")
--file.private-key string name of the file to export the private key data to (default "private.pem")
-h, --help help for generate
-l, --locality strings certificate locality
--not-before string earliest date and time the certificate is considered valid formatted as Jan 2 15:04:05 2006 (default is now)
--not-after string latest date and time the certificate is considered valid in various formats
--not-before string earliest date and time the certificate is considered valid in various formats (default is now)
-o, --organization strings certificate organization (default [Authelia])
--organizational-unit strings certificate organizational unit
--path.ca string source directory of the certificate authority files, if not provided the certificate will be self-signed

View File

@ -39,12 +39,13 @@ authelia crypto certificate ecdsa request --help
--country strings certificate country
-b, --curve string Sets the elliptic curve which can be P224, P256, P384, or P521 (default "P256")
-d, --directory string directory where the generated keys, certificates, etc will be stored
--duration duration duration of time the certificate is valid for (default 8760h0m0s)
--duration string duration of time the certificate is valid for (default "1y")
--file.csr string name of the file to export the certificate request data to (default "request.csr")
--file.private-key string name of the file to export the private key data to (default "private.pem")
-h, --help help for request
-l, --locality strings certificate locality
--not-before string earliest date and time the certificate is considered valid formatted as Jan 2 15:04:05 2006 (default is now)
--not-after string latest date and time the certificate is considered valid in various formats
--not-before string earliest date and time the certificate is considered valid in various formats (default is now)
-o, --organization strings certificate organization (default [Authelia])
--organizational-unit strings certificate organizational unit
-p, --postcode strings certificate postcode

View File

@ -35,19 +35,22 @@ authelia crypto certificate ed25519 request --help
### Options
```
--bundle enables generating the certificate bundle if the --path.ca flag is set
--ca create the certificate as a certificate authority certificate
-n, --common-name string certificate common name
--country strings certificate country
-d, --directory string directory where the generated keys, certificates, etc will be stored
--duration duration duration of time the certificate is valid for (default 8760h0m0s)
--duration string duration of time the certificate is valid for (default "1y")
--extended-usage strings specify the extended usage types of the certificate
--file.ca-certificate string certificate authority certificate to use when signing this certificate (default "ca.public.crt")
--file.ca-private-key string certificate authority private key to use to signing this certificate (default "ca.private.pem")
--file.certificate string name of the file to export the certificate data to (default "public.crt")
--file.certificate-bundle string name of the file to export the certificate bundle data to when the --bundle flag is set (default "public.bundle.crt")
--file.private-key string name of the file to export the private key data to (default "private.pem")
-h, --help help for generate
-l, --locality strings certificate locality
--not-before string earliest date and time the certificate is considered valid formatted as Jan 2 15:04:05 2006 (default is now)
--not-after string latest date and time the certificate is considered valid in various formats
--not-before string earliest date and time the certificate is considered valid in various formats (default is now)
-o, --organization strings certificate organization (default [Authelia])
--organizational-unit strings certificate organizational unit
--path.ca string source directory of the certificate authority files, if not provided the certificate will be self-signed

View File

@ -38,12 +38,13 @@ authelia crypto certificate ed25519 request --help
-n, --common-name string certificate common name
--country strings certificate country
-d, --directory string directory where the generated keys, certificates, etc will be stored
--duration duration duration of time the certificate is valid for (default 8760h0m0s)
--duration string duration of time the certificate is valid for (default "1y")
--file.csr string name of the file to export the certificate request data to (default "request.csr")
--file.private-key string name of the file to export the private key data to (default "private.pem")
-h, --help help for request
-l, --locality strings certificate locality
--not-before string earliest date and time the certificate is considered valid formatted as Jan 2 15:04:05 2006 (default is now)
--not-after string latest date and time the certificate is considered valid in various formats
--not-before string earliest date and time the certificate is considered valid in various formats (default is now)
-o, --organization strings certificate organization (default [Authelia])
--organizational-unit strings certificate organizational unit
-p, --postcode strings certificate postcode

View File

@ -36,19 +36,22 @@ authelia crypto certificate rsa generate --help
```
-b, --bits int number of RSA bits for the certificate (default 2048)
--bundle enables generating the certificate bundle if the --path.ca flag is set
--ca create the certificate as a certificate authority certificate
-n, --common-name string certificate common name
--country strings certificate country
-d, --directory string directory where the generated keys, certificates, etc will be stored
--duration duration duration of time the certificate is valid for (default 8760h0m0s)
--duration string duration of time the certificate is valid for (default "1y")
--extended-usage strings specify the extended usage types of the certificate
--file.ca-certificate string certificate authority certificate to use when signing this certificate (default "ca.public.crt")
--file.ca-private-key string certificate authority private key to use to signing this certificate (default "ca.private.pem")
--file.certificate string name of the file to export the certificate data to (default "public.crt")
--file.certificate-bundle string name of the file to export the certificate bundle data to when the --bundle flag is set (default "public.bundle.crt")
--file.private-key string name of the file to export the private key data to (default "private.pem")
-h, --help help for generate
-l, --locality strings certificate locality
--not-before string earliest date and time the certificate is considered valid formatted as Jan 2 15:04:05 2006 (default is now)
--not-after string latest date and time the certificate is considered valid in various formats
--not-before string earliest date and time the certificate is considered valid in various formats (default is now)
-o, --organization strings certificate organization (default [Authelia])
--organizational-unit strings certificate organizational unit
--path.ca string source directory of the certificate authority files, if not provided the certificate will be self-signed

View File

@ -39,12 +39,13 @@ authelia crypto certificate rsa request --help
-n, --common-name string certificate common name
--country strings certificate country
-d, --directory string directory where the generated keys, certificates, etc will be stored
--duration duration duration of time the certificate is valid for (default 8760h0m0s)
--duration string duration of time the certificate is valid for (default "1y")
--file.csr string name of the file to export the certificate request data to (default "request.csr")
--file.private-key string name of the file to export the private key data to (default "private.pem")
-h, --help help for request
-l, --locality strings certificate locality
--not-before string earliest date and time the certificate is considered valid formatted as Jan 2 15:04:05 2006 (default is now)
--not-after string latest date and time the certificate is considered valid in various formats
--not-before string earliest date and time the certificate is considered valid in various formats (default is now)
-o, --organization strings certificate organization (default [Authelia])
--organizational-unit strings certificate organizational unit
-p, --postcode strings certificate postcode

View File

@ -533,18 +533,16 @@ const (
storageMigrateDirectionDown = "down"
)
const (
timeLayoutCertificateNotBefore = "Jan 2 15:04:05 2006"
)
const (
cmdFlagNameDirectory = "directory"
cmdFlagNamePathCA = "path.ca"
cmdFlagNameBundle = "bundle"
cmdFlagNameFilePrivateKey = "file.private-key"
cmdFlagNameFilePublicKey = "file.public-key"
cmdFlagNameFileCertificate = "file.certificate"
cmdFlagNameFileCertificateBundle = "file.certificate-bundle"
cmdFlagNameFileCAPrivateKey = "file.ca-private-key"
cmdFlagNameFileCACertificate = "file.ca-certificate"
cmdFlagNameFileCSR = "file.csr"
@ -564,6 +562,7 @@ const (
cmdFlagNamePostcode = "postcode"
cmdFlagNameNotBefore = "not-before"
cmdFlagNameNotAfter = "not-after"
cmdFlagNameDuration = "duration"
cmdFlagNamePKCS8 = "pkcs8"
@ -749,4 +748,27 @@ Secrets:
way: 'key', 'secret', 'password', 'token'.
The available options and the specific secret mapping can be found here: https://www.authelia.com/configuration/methods/secrets/`
helpTopicTimeLayouts = `Several commands take date time inputs which are parsed. These inputs are parsed with
specific layouts in mind and these layouts are handled in order.
Format:
The layouts use a format where specific sequence of characters are representative of a portion of each timestamp.
See the go documentation for more information on how these layouts work, however the layouts are fairly self
explanatory and you can just use standard unix timestamps if desired.
Layouts:
Unix (µs): 1675899060000000
Unix (ms): 1675899060000
Unix (s): 1675899060
Simple: Jan 2 15:04:05 2006
Date Time: 2006-01-02 15:04:05
RFC3339: 2006-01-02T15:04:05Z07:00
RFC1123 with numeric timezone: Mon, 02 Jan 2006 15:04:05 -0700
Ruby Date: Mon Jan 02 15:04:05 -0700 2006
ANSIC: Mon Jan _2 15:04:05 2006
Date: 2006-01-02`
)

View File

@ -313,7 +313,7 @@ func (ctx *CmdCtx) CryptoCertificateRequestRunE(cmd *cobra.Command, _ []string)
b.WriteString(fmt.Sprintf("\n\tSubject Alternative Names: %s\n\n", strings.Join(cryptoSANsToString(template.DNSNames, template.IPAddresses), ", ")))
if privateKeyPath, csrPath, err = cryptoGetWritePathsFromCmd(cmd); err != nil {
if _, privateKeyPath, csrPath, err = cryptoGetWritePathsFromCmd(cmd); err != nil {
return err
}
@ -329,15 +329,11 @@ func (ctx *CmdCtx) CryptoCertificateRequestRunE(cmd *cobra.Command, _ []string)
return fmt.Errorf("failed to create certificate request: %w", err)
}
if privateKeyPath, csrPath, err = cryptoGetWritePathsFromCmd(cmd); err != nil {
return err
}
if err = utils.WriteKeyToPEM(privateKey, privateKeyPath, false); err != nil {
return err
}
if err = utils.WriteCertificateBytesToPEM(csr, csrPath, true); err != nil {
if err = utils.WriteCertificateBytesToPEM(csrPath, true, csr); err != nil {
return err
}
@ -406,21 +402,23 @@ func (ctx *CmdCtx) CryptoCertificateGenerateRunE(cmd *cobra.Command, _ []string,
b.WriteString(fmt.Sprintf("\n\tSubject Alternative Names: %s\n\n", strings.Join(cryptoSANsToString(template.DNSNames, template.IPAddresses), ", ")))
var (
privateKeyPath, certificatePath string
dir, privateKeyPath, certificatePath, certificateBundlePath string
bundle bool
certificate []byte
)
if privateKeyPath, certificatePath, err = cryptoGetWritePathsFromCmd(cmd); err != nil {
if dir, privateKeyPath, certificatePath, err = cryptoGetWritePathsFromCmd(cmd); err != nil {
return err
}
if bundle, certificateBundlePath, err = cryptoGetCertificateBundleFromCmd(cmd, dir, caCertificate); err != nil {
return err
}
b.WriteString("Output Paths:\n")
b.WriteString(fmt.Sprintf("\tPrivate Key: %s\n", privateKeyPath))
b.WriteString(fmt.Sprintf("\tCertificate: %s\n\n", certificatePath))
fmt.Print(b.String())
b.Reset()
b.WriteString(fmt.Sprintf("\tCertificate: %s\n", certificatePath))
if certificate, err = x509.CreateCertificate(ctx.providers.Random, template, parent, publicKey, signatureKey); err != nil {
return fmt.Errorf("failed to create certificate: %w", err)
@ -430,10 +428,24 @@ func (ctx *CmdCtx) CryptoCertificateGenerateRunE(cmd *cobra.Command, _ []string,
return err
}
if err = utils.WriteCertificateBytesToPEM(certificate, certificatePath, false); err != nil {
if err = utils.WriteCertificateBytesToPEM(certificatePath, false, certificate); err != nil {
return err
}
if bundle {
b.WriteString(fmt.Sprintf("\tCertificate (bundle): %s\n", certificateBundlePath))
if err = utils.WriteCertificateBytesToPEM(certificateBundlePath, false, certificate, caCertificate.Raw); err != nil {
return err
}
}
b.WriteString("\n")
fmt.Print(b.String())
b.Reset()
return nil
}
@ -448,7 +460,7 @@ func (ctx *CmdCtx) CryptoPairGenerateRunE(cmd *cobra.Command, _ []string, privat
return err
}
if privateKeyPath, publicKeyPath, err = cryptoGetWritePathsFromCmd(cmd); err != nil {
if _, privateKeyPath, publicKeyPath, err = cryptoGetWritePathsFromCmd(cmd); err != nil {
return err
}

View File

@ -32,8 +32,9 @@ func cmdFlagsCryptoCertificateCommon(cmd *cobra.Command) {
cmd.Flags().StringSliceP(cmdFlagNameStreetAddress, "s", nil, "certificate street address")
cmd.Flags().StringSliceP(cmdFlagNamePostcode, "p", nil, "certificate postcode")
cmd.Flags().String(cmdFlagNameNotBefore, "", fmt.Sprintf("earliest date and time the certificate is considered valid formatted as %s (default is now)", timeLayoutCertificateNotBefore))
cmd.Flags().Duration(cmdFlagNameDuration, 365*24*time.Hour, "duration of time the certificate is valid for")
cmd.Flags().String(cmdFlagNameNotBefore, "", "earliest date and time the certificate is considered valid in various formats (default is now)")
cmd.Flags().String(cmdFlagNameNotAfter, "", "latest date and time the certificate is considered valid in various formats")
cmd.Flags().String(cmdFlagNameDuration, "1y", "duration of time the certificate is valid for")
cmd.Flags().StringSlice(cmdFlagNameSANs, nil, "subject alternative names")
}
@ -42,6 +43,8 @@ func cmdFlagsCryptoCertificateGenerate(cmd *cobra.Command) {
cmd.Flags().String(cmdFlagNameFileCAPrivateKey, "ca.private.pem", "certificate authority private key to use to signing this certificate")
cmd.Flags().String(cmdFlagNameFileCACertificate, "ca.public.crt", "certificate authority certificate to use when signing this certificate")
cmd.Flags().String(cmdFlagNameFileCertificate, "public.crt", "name of the file to export the certificate data to")
cmd.Flags().String(cmdFlagNameFileCertificateBundle, "public.bundle.crt", fmt.Sprintf("name of the file to export the certificate bundle data to when the --%s flag is set", cmdFlagNameBundle))
cmd.Flags().Bool(cmdFlagNameBundle, false, fmt.Sprintf("enables generating the certificate bundle if the --%s flag is set", cmdFlagNamePathCA))
cmd.Flags().StringSlice(cmdFlagNameExtendedUsage, nil, "specify the extended usage types of the certificate")
@ -91,17 +94,15 @@ func cryptoSANsToString(dnsSANs []string, ipSANs []net.IP) (sans []string) {
return sans
}
func cryptoGetWritePathsFromCmd(cmd *cobra.Command) (privateKey, publicKey string, err error) {
var dir string
func cryptoGetWritePathsFromCmd(cmd *cobra.Command) (dir, privateKey, publicKey string, err error) {
if dir, err = cmd.Flags().GetString(cmdFlagNameDirectory); err != nil {
return "", "", err
return "", "", "", err
}
ca, _ := cmd.Flags().GetBool(cmdFlagNameCA)
csr := cmd.Use == cmdUseRequest
var private, public string
var pathPrivate, pathPublic string
var flagPrivate, flagPublic string
@ -118,15 +119,15 @@ func cryptoGetWritePathsFromCmd(cmd *cobra.Command) (privateKey, publicKey strin
flagPrivate, flagPublic = cmdFlagNameFilePrivateKey, cmdFlagNameFileCertificate
}
if private, err = cmd.Flags().GetString(flagPrivate); err != nil {
return "", "", err
if pathPrivate, err = cmd.Flags().GetString(flagPrivate); err != nil {
return "", "", "", err
}
if public, err = cmd.Flags().GetString(flagPublic); err != nil {
return "", "", err
if pathPublic, err = cmd.Flags().GetString(flagPublic); err != nil {
return "", "", "", err
}
return filepath.Join(dir, private), filepath.Join(dir, public), nil
return dir, filepath.Join(dir, pathPrivate), filepath.Join(dir, pathPublic), nil
}
func (ctx *CmdCtx) cryptoGenPrivateKeyFromCmd(cmd *cobra.Command) (privateKey any, err error) {
@ -169,6 +170,28 @@ func (ctx *CmdCtx) cryptoGenPrivateKeyFromCmd(cmd *cobra.Command) (privateKey an
return privateKey, nil
}
func cryptoGetCertificateBundleFromCmd(cmd *cobra.Command, dir string, caCertificate *x509.Certificate) (bundle bool, bundlePath string, err error) {
if bundle, err = cmd.Flags().GetBool(cmdFlagNameBundle); err != nil {
return false, "", err
}
if !bundle {
return bundle, bundlePath, err
}
if caCertificate == nil {
return false, "", fmt.Errorf("the --%s flag can't be used with self-signed certificates, you can specify the authority path using the --%s flag", cmdFlagNameBundle, cmdFlagNamePathCA)
}
if bundlePath, err = cmd.Flags().GetString(cmdFlagNameFileCertificateBundle); err != nil {
return false, "", err
}
bundlePath = filepath.Join(dir, bundlePath)
return bundle, bundlePath, err
}
func cryptoGetCAFromCmd(cmd *cobra.Command) (privateKey any, cert *x509.Certificate, err error) {
if !cmd.Flags().Changed(cmdFlagNamePathCA) {
return nil, nil, nil
@ -339,8 +362,8 @@ func (ctx *CmdCtx) cryptoGetCertificateFromCmd(cmd *cobra.Command) (certificate
var (
ca bool
subject *pkix.Name
notBeforeStr string
duration time.Duration
notBefore, notAfter time.Time
)
if ca, err = cmd.Flags().GetBool(cmdFlagNameCA); err != nil {
@ -351,30 +374,16 @@ func (ctx *CmdCtx) cryptoGetCertificateFromCmd(cmd *cobra.Command) (certificate
return nil, err
}
if notBeforeStr, err = cmd.Flags().GetString(cmdFlagNameNotBefore); err != nil {
return nil, err
}
if duration, err = cmd.Flags().GetDuration(cmdFlagNameDuration); err != nil {
if notBefore, notAfter, err = cryptoCertificateValidityFromCmd(cmd); err != nil {
return nil, err
}
var (
notBefore time.Time
serialNumber *big.Int
dnsSANs, extKeyUsages []string
ipSANs []net.IP
)
switch len(notBeforeStr) {
case 0:
notBefore = time.Now()
default:
if notBefore, err = time.Parse(timeLayoutCertificateNotBefore, notBeforeStr); err != nil {
return nil, fmt.Errorf("failed to parse not before: %w", err)
}
}
serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
if serialNumber, err = ctx.providers.Random.IntErr(serialNumberLimit); err != nil {
@ -396,7 +405,7 @@ func (ctx *CmdCtx) cryptoGetCertificateFromCmd(cmd *cobra.Command) (certificate
Subject: *subject,
NotBefore: notBefore,
NotAfter: notBefore.Add(duration),
NotAfter: notAfter,
IsCA: ca,
@ -415,6 +424,57 @@ func (ctx *CmdCtx) cryptoGetCertificateFromCmd(cmd *cobra.Command) (certificate
return certificate, nil
}
func cryptoCertificateValidityFromCmd(cmd *cobra.Command) (notBefore, notAfter time.Time, err error) {
never := time.UnixMicro(0)
switch cmd.Flags().Changed(cmdFlagNameNotBefore) {
case true:
var notBeforeStr string
if notBeforeStr, err = cmd.Flags().GetString(cmdFlagNameNotBefore); err != nil {
return never, never, err
}
if notBefore, err = utils.ParseTimeString(notBeforeStr); err != nil {
return never, never, fmt.Errorf("failed to parse not before: %w", err)
}
default:
notBefore = time.Now()
}
switch useNotAfter := cmd.Flags().Changed(cmdFlagNameNotAfter); {
case useNotAfter && cmd.Flags().Changed(cmdFlagNameDuration):
return never, never, fmt.Errorf("failed to determine not after ")
case useNotAfter:
var notAfterStr string
if notAfterStr, err = cmd.Flags().GetString(cmdFlagNameNotAfter); err != nil {
return never, never, err
}
if notAfter, err = utils.ParseTimeString(notAfterStr); err != nil {
return never, never, fmt.Errorf("failed to parse not after: %w", err)
}
default:
var (
durationStr string
duration time.Duration
)
if durationStr, err = cmd.Flags().GetString(cmdFlagNameDuration); err != nil {
return never, never, err
}
if duration, err = utils.ParseDurationString(durationStr); err != nil {
return never, never, fmt.Errorf("failed to parse duration string: %w", err)
}
notAfter = notBefore.Add(duration)
}
return notBefore, notAfter, nil
}
func fmtCryptoHashUse(use string) string {
switch use {
case cmdUseHashArgon2:

View File

@ -58,6 +58,7 @@ func NewRootCmd() (cmd *cobra.Command) {
newHelpTopic("config", "Help for the config file/directory paths", helpTopicConfig),
newHelpTopic("filters", "help topic for the config filters", helpTopicConfigFilters),
newHelpTopic("time-layouts", "help topic for the various time layouts", helpTopicTimeLayouts),
)
return cmd

View File

@ -160,7 +160,7 @@ func TestShouldRaiseErrorOnInvalidCertificatesDirectory(t *testing.T) {
func TestShouldNotRaiseErrorOnValidCertificatesDirectory(t *testing.T) {
validator := schema.NewStructValidator()
config := newDefaultConfig()
config.CertificatesDirectory = "../../suites/common/ssl"
config.CertificatesDirectory = "../../suites/common/pki"
ValidateConfiguration(&config, validator)

View File

@ -10,8 +10,8 @@ default_redirection_url: https://home.example.com:8080/
server:
port: 9091
tls:
certificate: /config/ssl/cert.pem
key: /config/ssl/key.pem
certificate: /config/ssl/public.backend.crt
key: /config/ssl/private.backend.pem
log:
level: debug

View File

@ -4,5 +4,5 @@ services:
authelia-backend:
volumes:
- './ActiveDirectory/configuration.yml:/config/configuration.yml:ro'
- './common/ssl:/config/ssl:ro'
- './common/pki:/config/ssl:ro'
...

View File

@ -6,8 +6,8 @@
server:
port: 9091
tls:
certificate: /config/ssl/cert.pem
key: /config/ssl/key.pem
certificate: /config/ssl/public.backend.crt
key: /config/ssl/private.backend.pem
log:
level: debug

View File

@ -5,5 +5,5 @@ services:
volumes:
- './BypassAll/configuration.yml:/config/configuration.yml:ro'
- './BypassAll/users.yml:/config/users.yml'
- './common/ssl:/config/ssl:ro'
- './common/pki:/config/ssl:ro'
...

View File

@ -6,8 +6,8 @@
server:
port: 9091
tls:
certificate: /config/ssl/cert.pem
key: /config/ssl/key.pem
certificate: /config/ssl/public.backend.crt
key: /config/ssl/private.backend.pem
log:
level: debug

View File

@ -6,7 +6,7 @@ services:
- './CLI/configuration.yml:/config/configuration.yml:ro'
- './CLI/storage.yml:/config/configuration.storage.yml:ro'
- './CLI/users.yml:/config/users.yml'
- './common/ssl:/config/ssl:ro'
- './common/pki:/config/ssl:ro'
- '/tmp:/tmp'
user: ${USER_ID}:${GROUP_ID}
...

View File

@ -9,8 +9,8 @@ server:
port: 9091
asset_path: /config/assets/
tls:
certificate: /config/ssl/cert.pem
key: /config/ssl/key.pem
certificate: /config/ssl/public.backend.crt
key: /config/ssl/private.backend.pem
endpoints:
authz:
caddy:

View File

@ -5,5 +5,5 @@ services:
volumes:
- './Caddy/configuration.yml:/config/configuration.yml:ro'
- './Caddy/users.yml:/config/users.yml'
- './common/ssl:/config/ssl:ro'
- './common/pki:/config/ssl:ro'
...

View File

@ -9,8 +9,8 @@ default_redirection_url: https://home.example.com:8080/
server:
port: 9091
tls:
certificate: /config/ssl/cert.pem
key: /config/ssl/key.pem
certificate: /config/ssl/public.backend.crt
key: /config/ssl/private.backend.pem
log:
level: debug

View File

@ -5,5 +5,5 @@ services:
volumes:
- './Docker/configuration.yml:/config/configuration.yml:ro'
- './Docker/users.yml:/config/users.yml'
- './common/ssl:/config/ssl:ro'
- './common/pki:/config/ssl:ro'
...

View File

@ -9,8 +9,8 @@ default_redirection_url: https://home.example.com:8080/
server:
port: 9091
tls:
certificate: /config/ssl/cert.pem
key: /config/ssl/key.pem
certificate: /config/ssl/public.backend.crt
key: /config/ssl/private.backend.pem
log:
level: trace

View File

@ -5,7 +5,7 @@ services:
volumes:
- './DuoPush/configuration.yml:/config/configuration.yml:ro'
- './DuoPush/users.yml:/config/users.yml'
- './common/ssl:/config/ssl:ro'
- './common/pki:/config/ssl:ro'
- '/tmp:/tmp'
user: ${USER_ID}:${GROUP_ID}
...

View File

@ -9,8 +9,8 @@ server:
port: 9091
asset_path: /config/assets/
tls:
certificate: /config/ssl/cert.pem
key: /config/ssl/key.pem
certificate: /config/ssl/public.backend.crt
key: /config/ssl/private.backend.pem
endpoints:
authz:
ext-authz:

View File

@ -5,5 +5,5 @@ services:
volumes:
- './Envoy/configuration.yml:/config/configuration.yml:ro'
- './Envoy/users.yml:/config/users.yml'
- './common/ssl:/config/ssl:ro'
- './common/pki:/config/ssl:ro'
...

View File

@ -8,8 +8,8 @@ jwt_secret: unsecure_secret
server:
port: 9091
tls:
certificate: /config/ssl/cert.pem
key: /config/ssl/key.pem
certificate: /config/ssl/public.backend.crt
key: /config/ssl/private.backend.pem
log:
level: debug

View File

@ -5,5 +5,5 @@ services:
volumes:
- './HAProxy/configuration.yml:/config/configuration.yml:ro'
- './HAProxy/users.yml:/config/users.yml'
- './common/ssl:/config/ssl:ro'
- './common/pki:/config/ssl:ro'
...

View File

@ -8,8 +8,8 @@ jwt_secret: unsecure_secret
server:
port: 9091
tls:
certificate: /config/ssl/cert.pem
key: /config/ssl/key.pem
certificate: /config/ssl/public.backend.crt
key: /config/ssl/private.backend.pem
log:
level: debug

View File

@ -4,5 +4,5 @@ services:
authelia-backend:
volumes:
- './HighAvailability/configuration.yml:/config/configuration.yml:ro'
- './common/ssl:/config/ssl:ro'
- './common/pki:/config/ssl:ro'
...

View File

@ -10,8 +10,8 @@ default_redirection_url: https://home.example.com:8080/
server:
port: 9091
tls:
certificate: /config/ssl/cert.pem
key: /config/ssl/key.pem
certificate: /config/ssl/public.backend.crt
key: /config/ssl/private.backend.pem
log:
level: debug

View File

@ -4,5 +4,5 @@ services:
authelia-backend:
volumes:
- './LDAP/configuration.yml:/config/configuration.yml:ro'
- './common/ssl:/config/ssl:ro'
- './common/pki:/config/ssl:ro'
...

View File

@ -9,8 +9,8 @@ default_redirection_url: https://home.example.com:8080/
server:
port: 9091
tls:
certificate: /config/ssl/cert.pem
key: /config/ssl/key.pem
certificate: /config/ssl/public.backend.crt
key: /config/ssl/private.backend.pem
log:
level: debug

View File

@ -5,5 +5,5 @@ services:
volumes:
- './MariaDB/configuration.yml:/config/configuration.yml:ro'
- './MariaDB/users.yml:/config/users.yml'
- './common/ssl:/config/ssl:ro'
- './common/pki:/config/ssl:ro'
...

View File

@ -9,8 +9,8 @@ theme: auto
server:
port: 9091
tls:
certificate: /config/ssl/cert.pem
key: /config/ssl/key.pem
certificate: /config/ssl/public.backend.crt
key: /config/ssl/private.backend.pem
telemetry:
metrics:

View File

@ -5,5 +5,5 @@ services:
volumes:
- './MultiCookieDomain/configuration.yml:/config/configuration.yml:ro'
- './MultiCookieDomain/users.yml:/config/users.yml'
- './common/ssl:/config/ssl:ro'
- './common/pki:/config/ssl:ro'
...

View File

@ -6,8 +6,8 @@
server:
port: 9091
tls:
certificate: /config/ssl/cert.pem
key: /config/ssl/key.pem
certificate: /config/ssl/public.backend.crt
key: /config/ssl/private.backend.pem
log:
level: debug

View File

@ -5,5 +5,5 @@ services:
volumes:
- './MySQL/configuration.yml:/config/configuration.yml:ro'
- './MySQL/users.yml:/config/users.yml'
- './common/ssl:/config/ssl:ro'
- './common/pki:/config/ssl:ro'
...

View File

@ -6,8 +6,8 @@
server:
port: 9091
tls:
certificate: /config/ssl/cert.pem
key: /config/ssl/key.pem
certificate: /config/ssl/public.backend.crt
key: /config/ssl/private.backend.pem
log:
level: debug

View File

@ -5,5 +5,5 @@ services:
volumes:
- './NetworkACL/configuration.yml:/config/configuration.yml:ro'
- './NetworkACL/users.yml:/config/users.yml'
- './common/ssl:/config/ssl:ro'
- './common/pki:/config/ssl:ro'
...

View File

@ -2,8 +2,8 @@
server:
port: 9091
tls:
certificate: /config/ssl/cert.pem
key: /config/ssl/key.pem
certificate: /config/ssl/public.backend.crt
key: /config/ssl/private.backend.pem
log:
level: debug

View File

@ -6,5 +6,5 @@ services:
- './OIDC/configuration.yml:/config/configuration.yml:ro'
- './OIDC/users.yml:/config/users.yml'
- './OIDC/keypair/key.pem:/config/issuer.pem:ro'
- './common/ssl:/config/ssl:ro'
- './common/pki:/config/ssl:ro'
...

View File

@ -2,8 +2,8 @@
server:
port: 9091
tls:
certificate: /config/ssl/cert.pem
key: /config/ssl/key.pem
certificate: /config/ssl/public.backend.crt
key: /config/ssl/private.backend.pem
log:
level: debug

View File

@ -6,5 +6,5 @@ services:
- './OIDCTraefik/configuration.yml:/config/configuration.yml:ro'
- './OIDCTraefik/users.yml:/config/users.yml'
- './OIDCTraefik/keypair/key.pem:/config/issuer.pem:ro'
- './common/ssl:/config/ssl:ro'
- './common/pki:/config/ssl:ro'
...

View File

@ -9,8 +9,8 @@ default_redirection_url: https://home.example.com:8080/
server:
port: 9091
tls:
certificate: /config/ssl/cert.pem
key: /config/ssl/key.pem
certificate: /config/ssl/public.backend.crt
key: /config/ssl/private.backend.pem
log:
level: debug

View File

@ -5,5 +5,5 @@ services:
volumes:
- './OneFactorOnly/configuration.yml:/config/configuration.yml:ro'
- './OneFactorOnly/users.yml:/config/users.yml'
- './common/ssl:/config/ssl:ro'
- './common/pki:/config/ssl:ro'
...

View File

@ -9,8 +9,8 @@ server:
port: 9091
path: auth
tls:
certificate: /config/ssl/cert.pem
key: /config/ssl/key.pem
certificate: /config/ssl/public.backend.crt
key: /config/ssl/private.backend.pem
log:
level: debug

View File

@ -5,5 +5,5 @@ services:
volumes:
- './PathPrefix/configuration.yml:/config/configuration.yml:ro'
- './PathPrefix/users.yml:/config/users.yml'
- './common/ssl:/config/ssl:ro'
- './common/pki:/config/ssl:ro'
...

View File

@ -9,8 +9,8 @@ default_redirection_url: https://home.example.com:8080/
server:
port: 9091
tls:
certificate: /config/ssl/cert.pem
key: /config/ssl/key.pem
certificate: /config/ssl/public.backend.crt
key: /config/ssl/private.backend.pem
log:
level: debug

View File

@ -5,5 +5,5 @@ services:
volumes:
- './Postgres/configuration.yml:/config/configuration.yml:ro'
- './Postgres/users.yml:/config/users.yml'
- './common/ssl:/config/ssl:ro'
- './common/pki:/config/ssl:ro'
...

View File

@ -9,8 +9,8 @@ default_redirection_url: https://home.example.com:8080/
server:
port: 9091
tls:
certificate: /config/ssl/cert.pem
key: /config/ssl/key.pem
certificate: /config/ssl/public.backend.crt
key: /config/ssl/private.backend.pem
log:
level: debug

View File

@ -5,5 +5,5 @@ services:
volumes:
- './ShortTimeouts/configuration.yml:/config/configuration.yml:ro'
- './ShortTimeouts/users.yml:/config/users.yml'
- './common/ssl:/config/ssl:ro'
- './common/pki:/config/ssl:ro'
...

View File

@ -8,8 +8,8 @@ theme: auto
server:
port: 9091
tls:
certificate: /config/ssl/cert.pem
key: /config/ssl/key.pem
certificate: /config/ssl/public.backend.crt
key: /config/ssl/private.backend.pem
telemetry:
metrics:

View File

@ -8,7 +8,7 @@ services:
volumes:
- './Standalone/configuration.yml:/config/configuration.yml:ro'
- './Standalone/users.yml:/config/users.yml'
- './common/ssl:/config/ssl:ro'
- './common/pki:/config/ssl:ro'
- '/tmp:/tmp'
user: ${USER_ID}:${GROUP_ID}
...

View File

@ -9,8 +9,8 @@ server:
port: 9091
asset_path: /config/assets/
tls:
certificate: /config/ssl/cert.pem
key: /config/ssl/key.pem
certificate: /config/ssl/public.backend.crt
key: /config/ssl/private.backend.pem
log:
level: debug

View File

@ -5,5 +5,5 @@ services:
volumes:
- './Traefik/configuration.yml:/config/configuration.yml:ro'
- './Traefik/users.yml:/config/users.yml'
- './common/ssl:/config/ssl:ro'
- './common/pki:/config/ssl:ro'
...

View File

@ -9,8 +9,8 @@ server:
port: 9091
asset_path: /config/assets/
tls:
certificate: /config/ssl/cert.pem
key: /config/ssl/key.pem
certificate: /config/ssl/public.backend.crt
key: /config/ssl/private.backend.pem
endpoints:
authz:
forward-auth:

View File

@ -7,5 +7,5 @@ services:
- './Traefik2/users.yml:/config/users.yml'
- './Traefik2/favicon.ico:/config/assets/favicon.ico'
- './Traefik2/logo.png:/config/assets/logo.png'
- './common/ssl:/config/ssl:ro'
- './common/pki:/config/ssl:ro'
...

View File

@ -0,0 +1,21 @@
-----BEGIN CERTIFICATE-----
MIIDhTCCAm2gAwIBAgIRAPl83YWFsuwIwxBRmdJyLLQwDQYJKoZIhvcNAQELBQAw
WzERMA8GA1UEChMIQXV0aGVsaWExFDASBgNVBAsTC0RldmVsb3BtZW50MTAwLgYD
VQQDEydBdXRoZWxpYSBEZXZlbG9wbWVudCBTdGFuZGFsb25lIFJvb3QgQ0EwIBcN
MDAwMTAxMDAwMDAwWhgPMjEwMDAxMDEwMDAwMDBaMFsxETAPBgNVBAoTCEF1dGhl
bGlhMRQwEgYDVQQLEwtEZXZlbG9wbWVudDEwMC4GA1UEAxMnQXV0aGVsaWEgRGV2
ZWxvcG1lbnQgU3RhbmRhbG9uZSBSb290IENBMIIBIjANBgkqhkiG9w0BAQEFAAOC
AQ8AMIIBCgKCAQEA2RtD74ISXHruAIIkIRTLGf5VK0b7iN5+CPW8qWjg74PCnid1
3DOqVCZ3HSXMP0iaH5rd+WAYojQo5Z1uZ75tXgzYjt6tyXG5H1nN1fkmjkHyNORP
abOZtngVaixvlT/hsONXszFdqogXhhI4DtEo0lvxJcnOHER4QVylM4YgDMF85jXi
VD893Y6Luik9B6FXLVK9iAJ5MfvD/r8kEPLsDTl2u/Ye0q4igVDJq9tOtb2enhlz
HtipYhzzNwEzQwy3tjzP9xpQG6XE6/JW20gQaBvoRBN64DMgRlh1/8ZVyYE8v/B1
vRVpSgmyCdDJeaRYZ6J+hO3LXBXU20CVZsM5VQIDAQABo0IwQDAOBgNVHQ8BAf8E
BAMCAqQwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUlrBVtyTWJQWRimLeZXr2
mrOzy2gwDQYJKoZIhvcNAQELBQADggEBAKXjAw5v8VTM6EDiUvR8XdiikYkycAG/
hcEt+QLkkBb72+tUNYbr57YJeJuqQcaPTBUQrIXsID8JV5dQJFfyIG2s3G0iuN70
W4fSRPqsSBIcyOK+2APLjkYV8qwLdh03Lyll4SZo7PCK8ItemsIK1NWhd74N49fm
+a8eyY5bgfA0FMkjY/ts4gAnYExGRoLOQRu/CgOvBlj2KQUrSNptze1rNlP32b63
eUv1wf/ajK2TxI1pQgkeu2lM3Tyu7q7J4UVn0UY0wtZvHtw2+UBGKZB3ok6ejBy2
HMjgLGuayGjhyUN8zRkuSvBynuI2wGhIlHklEbaQW5oFKbniXRqdzc4=
-----END CERTIFICATE-----

View File

@ -0,0 +1,5 @@
#!/bin/bash
go run ./cmd/authelia crypto certificate rsa generate --directory ./internal/suites/common/pki -n 'Authelia Development Standalone Root CA' --not-before 'Jan 1 00:00:00 2000' --not-after 'Jan 1 00:00:00 2100' -o 'Authelia' --organizational-unit 'Development' --ca
go run ./cmd/authelia crypto certificate rsa generate --directory ./internal/suites/common/pki --path.ca ./internal/suites/common/pki/ -n '*.example.com' --sans '*.example.com,example.com' --not-before 'Jan 1 00:00:00 2000' --not-after 'Jan 1 00:00:00 2100' -o 'Authelia' --organizational-unit 'Development' --bundle
go run ./cmd/authelia crypto certificate rsa generate --directory ./internal/suites/common/pki --path.ca ./internal/suites/common/pki/ --file.certificate public.backend.crt --file.certificate-bundle public.backend.bundle.crt --file.private-key private.backend.pem -n 'login.example.com' --sans 'login.example.com,authelia' --not-before 'Jan 1 00:00:00 2000' --not-after 'Jan 1 00:00:00 2100' -o 'Authelia' --organizational-unit 'Development' --bundle

View File

@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEA9sWvS9YsIUlm4w3ikVUjsCg1hbwyb/BSEGpRsVG8Oox0t62R
MEIG70lPfMg2IeJZDDdFreLru3WXjHgo4ovp6bcyDtSkE7sRMhw8sATmmHNI0/Mz
ImDS4r87LP5JqYY4iS6c24j41HaKBMhTEk2oQ0fXGOP4/q6Pzu4JClaF0ZGrQbdp
dL+5aPL/0UX2Y3Ybq08vRQ8X5LUrKdEnrBeChrbfybjhOyNtllt49CmUsRYQVro5
A2VTfroWSU7UfVUBPlI9UrATNvBmX+5fbctM6Go30dWsRXnlk4IcZHoUaA+6satK
HoWqzY9T4cQuNDSCTMe3TVT8VgYjrGWe9tQYRQIDAQABAoIBAGzgTXZFOVU6YHWf
I66BhOxs4ShvH0W6H0r0zbRRXp3qaHi90IgdKZgbnUFtyExs5GL6Y/jTXLRproKg
ZVGG755ZYHGgqf+2kmCsiq4Si3cJrkEtVQs+8uxt+Prh+rgb6wfDlBtcNITxA/YW
OGij8LA2mxV1R99+Px7KcRypjgMmx0Dp3NDoYE9Hj2VRVRLTo06WpZ7Mo9zfXtx6
MHeJwmSerWzERvUHmrX4hG8Do3cygYnd8EartBjCIa/qambXlz7uG1ip3/v05VJi
w28VUVEERASy2W/OJjwe8QSmUQ/1HzKz+Vv/WCEvVINQr/o4TwJeZrASQ6bj1JCa
CVR8mCUCgYEA+lZhEYnmuypNhJsBwQJJIvqIIF4zrRqojax5kWAiRDJY2AYAxozG
K/WP3NWrR+k+Q+wdzad9QBddLru1ykhBSPLGbvU2cupyiSDMg6R5vL4xcdWSD1Ej
VKSUxSgP0ue7GeMsTgr8vT59UG7R6Mc6+H5ZLt6q3DsOwqs8He6NSa8CgYEA/Fqp
HhaOSz85GWPtMUdsXJC14WiKmKDaCYxiQ9QUP8l7M8xPSiarhh9xc2EZDjSOR/SU
jmDSCFUvQHE0H9xdWp69W15bxnD6Z4uSrAFWThsRdp9kRckcKLUDuumoeOL3WI3x
gS49YS5uEP08oSPpsD4P98Llw+l/UimFI4RDHksCgYEAvpC86d4BL59fTT+2URgN
VrxCnek4C2FMyDRwgpMc4q805JWFR5/oR5RyRFi/P0m15Xy7n08N0Jk4jRfFpkvk
rsRo/BRcs5P3Rp4aMWgmZ+CcwRwkSNaqP4fd3EvH5/QRgP5nPq2sgd8tA+qojjwD
jyCXgU9t36JxdQ1nAR4Une8CgYEAx+7APGFcfUtq3q3n13oh5TiVkS+1VvVhqdz6
YYjePidQIBrH2xTGIm45AVO1eSa0b1fcdu1Immd7F1BZHsEFiW1o0sHwbklGatEO
9I3epeUWMehYll2enLXFbcn/uz7+/r7+zv0mjh8t/vHTnkuIsySInCBiz5PoVt0k
aZ45Wv8CgYAGj1ojQXs3SWQ+YY+rWrcMJ8atz9M4e7s8gEFiSp68ooi1kiVEKA2S
h4O79oksTAS1mgHiG237X6mJMkENpbPh8FR0hTcXbSyd7Ruki6h0LQnyXPGoV6KF
sojGWnJVDAtDMIT/z+tY4aeJbcicIgwS9oZ+dXQ/eQZiGKGOVexzwQ==
-----END RSA PRIVATE KEY-----

View File

@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEpQIBAAKCAQEA0XIlKyTMa1e+vaOnT8kTJza2lacZMoykLr4NmxcQnKRe09Xd
U6IqhsaaielJe2JdkzHKQID+F/jxlNWqbHU65KSPfMtJ59De4boZ0DjuzKoiCV77
HBj4mZg2PWEyerZU+JPLMhtIpnlbrzTeEjBpma51nXi/ELREYZsstEWNZ2RVaxwL
MKlAeDNVJotiLPMYA1rRJNa5Z3U9GPgy6l/F66NUd8klRuqJGeLDmlBu7NoRKhCX
DDk9VlATS5dBCde8rh/mGbkmNGrAa5sJFGaCZ9bDvroV31r2GdevbBY9+kXc3Vmi
eAlUarz5/B6jlV+CpwzKaGqcRSjEVMHobeb50wIDAQABAoIBAQCZSq/FefNbhScH
aSXlkUdBZhwQP/KDOshXpaYTorf3zZ3R8S9CLOhvKCCnW2T6ty3Bs6lRuJdAOGWo
BxHqu4pVGdQjBC6dShQ2pZyK94Bfrw2mSiwDjos072CTX/YwNg7OuGz5yVpM7Xmb
LFh0e5Zucm+Gx38f3LBHt+jqMBWkW0ir79kCn+pWzb5exMbr5khE1DbTLT4fu2he
1BYtq3aF3H1hxLsFGCSNBz0ocgTeRomrFH/gvn+WHBuS1GGR9gRFUqAC9ty4ej+l
yZ6n3Dt2bzNk7t/59s1FJoT4cYUQuxUnUxN+MHFIp9anDEUqO7yfw7GVFaLmj5lV
rnZNfV8RAoGBAPxc0cPO7eGvtFF26rJSacBqDvxLef4dEcRsyLHGbcLV2EhChSRy
ukHG8JZpDaG5dzFP0MRMNQ/TgrBOLypEH9QQgRDS+vx3xDQv/dtcsrQi1JTQ7K97
Nye3dZnH5YwW7SuZPcrBqc7NnGLWwAP9gJH9BULQEYPztkLimHMfrVMHAoGBANR2
+DMb3/TTtCI+XjWhUPMH7pgKjzP9UrkUYosRmSikAttJZYmWhy3sfDbzSm/y+G5r
aECJ3SwhdzRkUStUPi2z7L9zIdysKhXrZqxx3eiT9lX5en/CY57tTHU5U/Gco7ue
LToBLa/QVhUpQsM14GtTCx6kVt/1Ca9vMhKJhbPVAoGBAJKgY4h/bJuaeQx0KX0e
gS848CgKuoC45x/XjOwtvIxdr2Kcs7svpaSeMB9UPoVeuzA9jbDgDlx5qg9B/gly
t4OBa1wZGZcjBy7DS53uyC39psIoebyjvLIo1/1XtbmlgHu6d7qct0rfECQlXj9z
RQaNmkbtmZ+vNJC8E5OgLhZ1AoGAEMHlZ5qOOnAsJ/tFUPONP+3jOqiyjMYxLRnX
f+J+cv8knokWchimRs/9KBReTaAbShI8ocJ96EonbcFvyXwWf2JOjPcWt6H4m8v6
SCWe/AYXisZnjAYX92P+AMzpuwfAvj6GkKmhIdnLwFWFx4HLVouxN9R8aaOZBPY/
O8kO+5UCgYEAhyz0n7kvOqafNSgjTbXGoxTXWhgVEJYtEAd5xj5XzMeprVEY68+l
goJc6ww+Etq5/ghbtONW5d9Zy+BNcVMtZkNJr7j6XCzm0VEa6ZiKZK2HpZrkvbif
YQ3YOkW6583FXQCF67MuR2lbcDcLX9qgLT6MbAwiSL4Ow8qnLpz4CHA=
-----END RSA PRIVATE KEY-----

View File

@ -0,0 +1,43 @@
-----BEGIN CERTIFICATE-----
MIIDqzCCApOgAwIBAgIRAMThBFxQ+GQhzdihMNsQxgEwDQYJKoZIhvcNAQELBQAw
WzERMA8GA1UEChMIQXV0aGVsaWExFDASBgNVBAsTC0RldmVsb3BtZW50MTAwLgYD
VQQDEydBdXRoZWxpYSBEZXZlbG9wbWVudCBTdGFuZGFsb25lIFJvb3QgQ0EwIBcN
MDAwMTAxMDAwMDAwWhgPMjEwMDAxMDEwMDAwMDBaMEUxETAPBgNVBAoTCEF1dGhl
bGlhMRQwEgYDVQQLEwtEZXZlbG9wbWVudDEaMBgGA1UEAxMRbG9naW4uZXhhbXBs
ZS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQD2xa9L1iwhSWbj
DeKRVSOwKDWFvDJv8FIQalGxUbw6jHS3rZEwQgbvSU98yDYh4lkMN0Wt4uu7dZeM
eCjii+nptzIO1KQTuxEyHDywBOaYc0jT8zMiYNLivzss/kmphjiJLpzbiPjUdooE
yFMSTahDR9cY4/j+ro/O7gkKVoXRkatBt2l0v7lo8v/RRfZjdhurTy9FDxfktSsp
0SesF4KGtt/JuOE7I22WW3j0KZSxFhBWujkDZVN+uhZJTtR9VQE+Uj1SsBM28GZf
7l9ty0zoajfR1axFeeWTghxkehRoD7qxq0oeharNj1PhxC40NIJMx7dNVPxWBiOs
ZZ721BhFAgMBAAGjfjB8MA4GA1UdDwEB/wQEAwIFoDATBgNVHSUEDDAKBggrBgEF
BQcDATAMBgNVHRMBAf8EAjAAMB8GA1UdIwQYMBaAFJawVbck1iUFkYpi3mV69pqz
s8toMCYGA1UdEQQfMB2CEWxvZ2luLmV4YW1wbGUuY29tgghhdXRoZWxpYTANBgkq
hkiG9w0BAQsFAAOCAQEALqCLqIAM8wi0M12hMs4dARRtEEAZm/ff8B18SgBuES79
qeas6pggPRmpfM3Ogjk+t8qG02yQeAF9zqAFXaI46kQxQohKcCrfW96pX91Tvuwc
x+3xsnHWj+FoI99uUGldDjVJoePXXB7wKB3/26o7dlcWry7y04cURvD3v/v/zABw
hPB5+t+5lB+kGSZg7ChnCDBLJx8/y9JTHKbZL3kHenvPHOn+T6aRQKNmAGpGwqV4
Eq+lB/YKU9JADarOF5FCTux677JO1XdDcnooGZmbUrKbjzO/NnbREjBRZYfqJiV6
uTz/I2MJ7IUiYh18xT3ZL2oUbnWly+TnR9oWnwSqLg==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIDhTCCAm2gAwIBAgIRAPl83YWFsuwIwxBRmdJyLLQwDQYJKoZIhvcNAQELBQAw
WzERMA8GA1UEChMIQXV0aGVsaWExFDASBgNVBAsTC0RldmVsb3BtZW50MTAwLgYD
VQQDEydBdXRoZWxpYSBEZXZlbG9wbWVudCBTdGFuZGFsb25lIFJvb3QgQ0EwIBcN
MDAwMTAxMDAwMDAwWhgPMjEwMDAxMDEwMDAwMDBaMFsxETAPBgNVBAoTCEF1dGhl
bGlhMRQwEgYDVQQLEwtEZXZlbG9wbWVudDEwMC4GA1UEAxMnQXV0aGVsaWEgRGV2
ZWxvcG1lbnQgU3RhbmRhbG9uZSBSb290IENBMIIBIjANBgkqhkiG9w0BAQEFAAOC
AQ8AMIIBCgKCAQEA2RtD74ISXHruAIIkIRTLGf5VK0b7iN5+CPW8qWjg74PCnid1
3DOqVCZ3HSXMP0iaH5rd+WAYojQo5Z1uZ75tXgzYjt6tyXG5H1nN1fkmjkHyNORP
abOZtngVaixvlT/hsONXszFdqogXhhI4DtEo0lvxJcnOHER4QVylM4YgDMF85jXi
VD893Y6Luik9B6FXLVK9iAJ5MfvD/r8kEPLsDTl2u/Ye0q4igVDJq9tOtb2enhlz
HtipYhzzNwEzQwy3tjzP9xpQG6XE6/JW20gQaBvoRBN64DMgRlh1/8ZVyYE8v/B1
vRVpSgmyCdDJeaRYZ6J+hO3LXBXU20CVZsM5VQIDAQABo0IwQDAOBgNVHQ8BAf8E
BAMCAqQwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUlrBVtyTWJQWRimLeZXr2
mrOzy2gwDQYJKoZIhvcNAQELBQADggEBAKXjAw5v8VTM6EDiUvR8XdiikYkycAG/
hcEt+QLkkBb72+tUNYbr57YJeJuqQcaPTBUQrIXsID8JV5dQJFfyIG2s3G0iuN70
W4fSRPqsSBIcyOK+2APLjkYV8qwLdh03Lyll4SZo7PCK8ItemsIK1NWhd74N49fm
+a8eyY5bgfA0FMkjY/ts4gAnYExGRoLOQRu/CgOvBlj2KQUrSNptze1rNlP32b63
eUv1wf/ajK2TxI1pQgkeu2lM3Tyu7q7J4UVn0UY0wtZvHtw2+UBGKZB3ok6ejBy2
HMjgLGuayGjhyUN8zRkuSvBynuI2wGhIlHklEbaQW5oFKbniXRqdzc4=
-----END CERTIFICATE-----

View File

@ -0,0 +1,22 @@
-----BEGIN CERTIFICATE-----
MIIDqzCCApOgAwIBAgIRAMThBFxQ+GQhzdihMNsQxgEwDQYJKoZIhvcNAQELBQAw
WzERMA8GA1UEChMIQXV0aGVsaWExFDASBgNVBAsTC0RldmVsb3BtZW50MTAwLgYD
VQQDEydBdXRoZWxpYSBEZXZlbG9wbWVudCBTdGFuZGFsb25lIFJvb3QgQ0EwIBcN
MDAwMTAxMDAwMDAwWhgPMjEwMDAxMDEwMDAwMDBaMEUxETAPBgNVBAoTCEF1dGhl
bGlhMRQwEgYDVQQLEwtEZXZlbG9wbWVudDEaMBgGA1UEAxMRbG9naW4uZXhhbXBs
ZS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQD2xa9L1iwhSWbj
DeKRVSOwKDWFvDJv8FIQalGxUbw6jHS3rZEwQgbvSU98yDYh4lkMN0Wt4uu7dZeM
eCjii+nptzIO1KQTuxEyHDywBOaYc0jT8zMiYNLivzss/kmphjiJLpzbiPjUdooE
yFMSTahDR9cY4/j+ro/O7gkKVoXRkatBt2l0v7lo8v/RRfZjdhurTy9FDxfktSsp
0SesF4KGtt/JuOE7I22WW3j0KZSxFhBWujkDZVN+uhZJTtR9VQE+Uj1SsBM28GZf
7l9ty0zoajfR1axFeeWTghxkehRoD7qxq0oeharNj1PhxC40NIJMx7dNVPxWBiOs
ZZ721BhFAgMBAAGjfjB8MA4GA1UdDwEB/wQEAwIFoDATBgNVHSUEDDAKBggrBgEF
BQcDATAMBgNVHRMBAf8EAjAAMB8GA1UdIwQYMBaAFJawVbck1iUFkYpi3mV69pqz
s8toMCYGA1UdEQQfMB2CEWxvZ2luLmV4YW1wbGUuY29tgghhdXRoZWxpYTANBgkq
hkiG9w0BAQsFAAOCAQEALqCLqIAM8wi0M12hMs4dARRtEEAZm/ff8B18SgBuES79
qeas6pggPRmpfM3Ogjk+t8qG02yQeAF9zqAFXaI46kQxQohKcCrfW96pX91Tvuwc
x+3xsnHWj+FoI99uUGldDjVJoePXXB7wKB3/26o7dlcWry7y04cURvD3v/v/zABw
hPB5+t+5lB+kGSZg7ChnCDBLJx8/y9JTHKbZL3kHenvPHOn+T6aRQKNmAGpGwqV4
Eq+lB/YKU9JADarOF5FCTux677JO1XdDcnooGZmbUrKbjzO/NnbREjBRZYfqJiV6
uTz/I2MJ7IUiYh18xT3ZL2oUbnWly+TnR9oWnwSqLg==
-----END CERTIFICATE-----

View File

@ -0,0 +1,43 @@
-----BEGIN CERTIFICATE-----
MIIDpjCCAo6gAwIBAgIRAIVWl6KyC+LSDXvq138YbKkwDQYJKoZIhvcNAQELBQAw
WzERMA8GA1UEChMIQXV0aGVsaWExFDASBgNVBAsTC0RldmVsb3BtZW50MTAwLgYD
VQQDEydBdXRoZWxpYSBEZXZlbG9wbWVudCBTdGFuZGFsb25lIFJvb3QgQ0EwIBcN
MDAwMTAxMDAwMDAwWhgPMjEwMDAxMDEwMDAwMDBaMEExETAPBgNVBAoTCEF1dGhl
bGlhMRQwEgYDVQQLEwtEZXZlbG9wbWVudDEWMBQGA1UEAwwNKi5leGFtcGxlLmNv
bTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANFyJSskzGtXvr2jp0/J
Eyc2tpWnGTKMpC6+DZsXEJykXtPV3VOiKobGmonpSXtiXZMxykCA/hf48ZTVqmx1
OuSkj3zLSefQ3uG6GdA47syqIgle+xwY+JmYNj1hMnq2VPiTyzIbSKZ5W6803hIw
aZmudZ14vxC0RGGbLLRFjWdkVWscCzCpQHgzVSaLYizzGANa0STWuWd1PRj4Mupf
xeujVHfJJUbqiRniw5pQbuzaESoQlww5PVZQE0uXQQnXvK4f5hm5JjRqwGubCRRm
gmfWw766Fd9a9hnXr2wWPfpF3N1ZongJVGq8+fweo5VfgqcMymhqnEUoxFTB6G3m
+dMCAwEAAaN9MHswDgYDVR0PAQH/BAQDAgWgMBMGA1UdJQQMMAoGCCsGAQUFBwMB
MAwGA1UdEwEB/wQCMAAwHwYDVR0jBBgwFoAUlrBVtyTWJQWRimLeZXr2mrOzy2gw
JQYDVR0RBB4wHIINKi5leGFtcGxlLmNvbYILZXhhbXBsZS5jb20wDQYJKoZIhvcN
AQELBQADggEBAKF2dga/ZrglllTHZBqQdf//DkHpHfW/awxkOYDGKFNAosIQhn5d
cBC6fctnPugPw+97J6IaggP6ZDC8umlARWwsImxU2A6/uDoTZGdN3xrr2i0GOvho
bfz5FKJFte4MNYUii+IeuXJrzK7mZYXOS0iJwRmVs7euI2EZNQwE6ckMILRX2CmR
6CmvCrxKYhxZoo2P/tV+x5LZELHZ654rmUcug0mSJrtM6Hai94JazmYVfLWzLqvW
bTNav1MLJzqm32O9nFIBWOzq1Z77gdW/VnPwMeKBDwC/6p05p8b4kdsc+HtRq77M
5cObzCy4GcKIIgW5ovlTiLkAmWIkCUxIKMU=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIDhTCCAm2gAwIBAgIRAPl83YWFsuwIwxBRmdJyLLQwDQYJKoZIhvcNAQELBQAw
WzERMA8GA1UEChMIQXV0aGVsaWExFDASBgNVBAsTC0RldmVsb3BtZW50MTAwLgYD
VQQDEydBdXRoZWxpYSBEZXZlbG9wbWVudCBTdGFuZGFsb25lIFJvb3QgQ0EwIBcN
MDAwMTAxMDAwMDAwWhgPMjEwMDAxMDEwMDAwMDBaMFsxETAPBgNVBAoTCEF1dGhl
bGlhMRQwEgYDVQQLEwtEZXZlbG9wbWVudDEwMC4GA1UEAxMnQXV0aGVsaWEgRGV2
ZWxvcG1lbnQgU3RhbmRhbG9uZSBSb290IENBMIIBIjANBgkqhkiG9w0BAQEFAAOC
AQ8AMIIBCgKCAQEA2RtD74ISXHruAIIkIRTLGf5VK0b7iN5+CPW8qWjg74PCnid1
3DOqVCZ3HSXMP0iaH5rd+WAYojQo5Z1uZ75tXgzYjt6tyXG5H1nN1fkmjkHyNORP
abOZtngVaixvlT/hsONXszFdqogXhhI4DtEo0lvxJcnOHER4QVylM4YgDMF85jXi
VD893Y6Luik9B6FXLVK9iAJ5MfvD/r8kEPLsDTl2u/Ye0q4igVDJq9tOtb2enhlz
HtipYhzzNwEzQwy3tjzP9xpQG6XE6/JW20gQaBvoRBN64DMgRlh1/8ZVyYE8v/B1
vRVpSgmyCdDJeaRYZ6J+hO3LXBXU20CVZsM5VQIDAQABo0IwQDAOBgNVHQ8BAf8E
BAMCAqQwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUlrBVtyTWJQWRimLeZXr2
mrOzy2gwDQYJKoZIhvcNAQELBQADggEBAKXjAw5v8VTM6EDiUvR8XdiikYkycAG/
hcEt+QLkkBb72+tUNYbr57YJeJuqQcaPTBUQrIXsID8JV5dQJFfyIG2s3G0iuN70
W4fSRPqsSBIcyOK+2APLjkYV8qwLdh03Lyll4SZo7PCK8ItemsIK1NWhd74N49fm
+a8eyY5bgfA0FMkjY/ts4gAnYExGRoLOQRu/CgOvBlj2KQUrSNptze1rNlP32b63
eUv1wf/ajK2TxI1pQgkeu2lM3Tyu7q7J4UVn0UY0wtZvHtw2+UBGKZB3ok6ejBy2
HMjgLGuayGjhyUN8zRkuSvBynuI2wGhIlHklEbaQW5oFKbniXRqdzc4=
-----END CERTIFICATE-----

View File

@ -0,0 +1,22 @@
-----BEGIN CERTIFICATE-----
MIIDpjCCAo6gAwIBAgIRAIVWl6KyC+LSDXvq138YbKkwDQYJKoZIhvcNAQELBQAw
WzERMA8GA1UEChMIQXV0aGVsaWExFDASBgNVBAsTC0RldmVsb3BtZW50MTAwLgYD
VQQDEydBdXRoZWxpYSBEZXZlbG9wbWVudCBTdGFuZGFsb25lIFJvb3QgQ0EwIBcN
MDAwMTAxMDAwMDAwWhgPMjEwMDAxMDEwMDAwMDBaMEExETAPBgNVBAoTCEF1dGhl
bGlhMRQwEgYDVQQLEwtEZXZlbG9wbWVudDEWMBQGA1UEAwwNKi5leGFtcGxlLmNv
bTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANFyJSskzGtXvr2jp0/J
Eyc2tpWnGTKMpC6+DZsXEJykXtPV3VOiKobGmonpSXtiXZMxykCA/hf48ZTVqmx1
OuSkj3zLSefQ3uG6GdA47syqIgle+xwY+JmYNj1hMnq2VPiTyzIbSKZ5W6803hIw
aZmudZ14vxC0RGGbLLRFjWdkVWscCzCpQHgzVSaLYizzGANa0STWuWd1PRj4Mupf
xeujVHfJJUbqiRniw5pQbuzaESoQlww5PVZQE0uXQQnXvK4f5hm5JjRqwGubCRRm
gmfWw766Fd9a9hnXr2wWPfpF3N1ZongJVGq8+fweo5VfgqcMymhqnEUoxFTB6G3m
+dMCAwEAAaN9MHswDgYDVR0PAQH/BAQDAgWgMBMGA1UdJQQMMAoGCCsGAQUFBwMB
MAwGA1UdEwEB/wQCMAAwHwYDVR0jBBgwFoAUlrBVtyTWJQWRimLeZXr2mrOzy2gw
JQYDVR0RBB4wHIINKi5leGFtcGxlLmNvbYILZXhhbXBsZS5jb20wDQYJKoZIhvcN
AQELBQADggEBAKF2dga/ZrglllTHZBqQdf//DkHpHfW/awxkOYDGKFNAosIQhn5d
cBC6fctnPugPw+97J6IaggP6ZDC8umlARWwsImxU2A6/uDoTZGdN3xrr2i0GOvho
bfz5FKJFte4MNYUii+IeuXJrzK7mZYXOS0iJwRmVs7euI2EZNQwE6ckMILRX2CmR
6CmvCrxKYhxZoo2P/tV+x5LZELHZ654rmUcug0mSJrtM6Hai94JazmYVfLWzLqvW
bTNav1MLJzqm32O9nFIBWOzq1Z77gdW/VnPwMeKBDwC/6p05p8b4kdsc+HtRq77M
5cObzCy4GcKIIgW5ovlTiLkAmWIkCUxIKMU=
-----END CERTIFICATE-----

View File

@ -1,19 +0,0 @@
-----BEGIN CERTIFICATE-----
MIIC/jCCAeagAwIBAgIRAKF0IRxC55eee6icERVf6fgwDQYJKoZIhvcNAQELBQAw
EjEQMA4GA1UEChMHQWNtZSBDbzAgFw0yMDAzMDExMjMzMzlaGA8yMTIwMDIwNjEy
MzMzOVowEjEQMA4GA1UEChMHQWNtZSBDbzCCASIwDQYJKoZIhvcNAQEBBQADggEP
ADCCAQoCggEBAMi7/oSazFIxP3rHsSLjw5XPnpMKEaVwU1zLRzW6W80BDa/ER5to
I3POGLv8lAhtUwB6WvyilrCZfs/D5lkcCxswafU/2LNppFuODnW+PG9eobgOy6Nv
f+KbnZFPRV7PB2yK6DqMyb+tbTQ7F6rEf4i6n28DI0dNyNvUCk0ld3o93LZBvC/D
/+Ulf3Vtdfsd2TckXvdA8lH4VGQJ+FIxhboTlbW8VJlk1V7FZef7+m867kOnPSaj
zv5yygrIA0XPaMAZC/SZrXHMdhvcs43fgmmTel7JD4Sy/Z/pmFlrZr5Xa8jcWycJ
ILLuPnXhgKstgq5wtDkTMZ6rpgMrKcjMKcMCAwEAAaNNMEswDgYDVR0PAQH/BAQD
AgWgMBMGA1UdJQQMMAoGCCsGAQUFBwMBMAwGA1UdEwEB/wQCMAAwFgYDVR0RBA8w
DYILZXhhbXBsZS5jb20wDQYJKoZIhvcNAQELBQADggEBABdWkbipzPivAvvamMmQ
5iPPeStfdr5MBxJGT9nPbeXdtS/13FJnspLBMMYOw/2AZk7VFrNjxkXc4NHZSlGz
FcGMlSO40fyirdYaQTDtS230ucLB+LzfZx37y9dKpEKVmQ151kKJjJ4hAZ47LmAQ
aFoDLRo7PA2HmnJ60GrI9wVp96uy1sQ6PcToIyMcVEQ/tLEEow+ykSeiZb9+qBKV
K9GUcu2LorhBtUMmEWs0TJElaf6eKUoG6JXM2byulDg24w5b9gC26kAlHWc5WDU5
pAXOjlN/OYHB0sDbYViWIL390376fYIfu2N5EDKY4QjEYsWEs4Wm9HVS9IgHP/Gi
Xbo=
-----END CERTIFICATE-----

View File

@ -1,28 +0,0 @@
-----BEGIN PRIVATE KEY-----
MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDIu/6EmsxSMT96
x7Ei48OVz56TChGlcFNcy0c1ulvNAQ2vxEebaCNzzhi7/JQIbVMAelr8opawmX7P
w+ZZHAsbMGn1P9izaaRbjg51vjxvXqG4Dsujb3/im52RT0Vezwdsiug6jMm/rW00
OxeqxH+Iup9vAyNHTcjb1ApNJXd6Pdy2Qbwvw//lJX91bXX7Hdk3JF73QPJR+FRk
CfhSMYW6E5W1vFSZZNVexWXn+/pvOu5Dpz0mo87+csoKyANFz2jAGQv0ma1xzHYb
3LON34Jpk3peyQ+Esv2f6ZhZa2a+V2vI3FsnCSCy7j514YCrLYKucLQ5EzGeq6YD
KynIzCnDAgMBAAECggEAC13R0LJvRWwyewJZvm8FQTNreEoGq8aLgeKk2p792cLo
gn5ry5n+/+y4q9RmkX+XRpynEE0omUFn09306jDTVCvOpCuEWsxtmR2XJgWqqGfE
Yoa78zo6FJvZNUQ22mKAuh23frFAL1FjsKRz96B+1EA1DPUxhzUZXZFJMAsiE9LZ
PxqPmnqXbPZsOb1XG33TAdCp6CC3H8KHICC+i4IC8prjKHGH/Q1saoNw8jmgwv0S
DelQUbEtqfmE6BmyTGxdeu4uW2Nv/wcENwySAOPKi5gstlbSKTa4IpKGp7CdquWi
stUW6pnSiEeDrDAzwC8uWdncOvnkAy2lRJkz/F9YoQKBgQDrCCqYdvGshecBBnfQ
fowxak2YBfG2jhAKPMHzrvQn5FIb+11x/jeXPEfOB6FShIzZ97JpFIpH3tcONlj3
OVzGCTD6WdRTcltzXVneJtNog7DliNFY4YmIPmQJ+y+EvJW1rSZTZAZI1Nbijg3n
fSd0PTzvgOGHSl1//RI1mFx7MwKBgQDapIPPSF0yf1UJ6Hhzam5NHGZ9fSqV5Qs0
Gi7uM08iDV5K7xiPglBkbN2EuMlgVnHaa5g8X897uwRSYR6nL4PRvcJiNSvnhWhe
+K3x7iHewIPYVfcghoqzuPKsXH2Zm26usdXHxBBa3IBbKtGaHnAd9h65AOUYAmAx
C2BzN90XMQKBgE2MjEFyPZunMulrsOziVG+Zm7ClhXOuvCwkj/pPp8/hzhXdgp+y
ObV09lxMuDX59l+VExEI7fd414yg8gngq3PMZJS2PxCpkvMlwhlCxk6d5ShXVHv3
LuH9dBS3BJ7PerZPQ24QeuJdF+n45S2UZgg8jHaaF9AEAYXRgsicVSdxAoGAJI0U
K/bg/awjv0BJwqGsRt/Ukm32TJC5ysAF0HRrajnp5YULChKy9dbtQV7S63QIHIeY
L5+kw/6DvnHV+gULeGjMsjZJXK8Ev7u6+JLivqZYZDYa1iknztvAVegwZxmA61t3
bantQgNSwerql2U3QQsAH9Vydw0On6RTP2+7WkECgYBWD3u64hBKmAxPkqPotkgI
w/jdOlv8FLHO79+oH1PtKvkzspcYaecKGDm/RNLIXLYnt0AmZEK4qQ4/zDFaR/rc
AhoxK2cKTRltMrhp1ivtFfLggVGogtYNxEnjnsD4KMvH3SjSNdt06YgtZ92++fOp
UsE8Mpf4/G5X7DmcHJHk+w==
-----END PRIVATE KEY-----

View File

@ -5,7 +5,9 @@ services:
image: envoyproxy/envoy:v1.25.1
volumes:
- ./example/compose/envoy/envoy.yaml:/etc/envoy/envoy.yaml
- ./example/compose/nginx/portal/ssl:/etc/ssl
- ./common/pki:/pki
ports:
- 9901:9901
networks:
authelianet:
ipv4_address: 192.168.240.100

View File

@ -1,4 +1,10 @@
---
# Enable the admin interface at http://192.168.240.100:9901/ for debugging.
admin:
address:
socket_address:
address: 0.0.0.0
port_value: 9901
static_resources:
listeners:
- name: listener_0
@ -78,6 +84,13 @@ static_resources:
- name: envoy.filters.http.ext_authz
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthz
transport_api_version: v3
allowed_headers:
patterns:
- exact: authorization
- exact: proxy-authorization
- exact: accept
- exact: cookie
http_service:
path_prefix: /api/authz/ext-authz/
server_uri:
@ -85,12 +98,6 @@ static_resources:
cluster: authelia-backend
timeout: 0.25s
authorization_request:
allowed_headers:
patterns:
- exact: authorization
- exact: proxy-authorization
- exact: accept
- exact: cookie
headers_to_add:
- key: X-Forwarded-Proto
value: '%REQ(:SCHEME)%'
@ -118,9 +125,9 @@ static_resources:
common_tls_context:
tls_certificates:
- certificate_chain:
filename: /etc/ssl/server.cert
filename: /pki/public.bundle.crt
private_key:
filename: /etc/ssl/server.key
filename: /pki/private.pem
clusters:
- name: authelia-frontend
transport_socket_matches:
@ -139,9 +146,9 @@ static_resources:
typed_config:
"@type": type.googleapis.com/envoy.extensions.transport_sockets.raw_buffer.v3.RawBuffer
connect_timeout: 0.25s
type: STRICT_DNS
type: strict_dns
dns_lookup_family: V4_ONLY
lb_policy: ROUND_ROBIN
lb_policy: round_robin
load_assignment:
cluster_name: authelia-frontend
endpoints:
@ -172,9 +179,9 @@ static_resources:
enableTLS: true
- name: authelia-backend
connect_timeout: 0.25s
type: LOGICAL_DNS
dns_lookup_family: V4_ONLY
lb_policy: ROUND_ROBIN
type: logical_dns
dns_lookup_family: v4_only
lb_policy: round_robin
load_assignment:
cluster_name: authelia-backend
endpoints:
@ -191,9 +198,9 @@ static_resources:
common_tls_context: {}
- name: smtp
connect_timeout: 0.25s
type: LOGICAL_DNS
dns_lookup_family: V4_ONLY
lb_policy: ROUND_ROBIN
type: logical_dns
dns_lookup_family: v4_only
lb_policy: round_robin
load_assignment:
cluster_name: smtp
endpoints:
@ -205,9 +212,9 @@ static_resources:
port_value: 1080
- name: httpbin
connect_timeout: 0.25s
type: LOGICAL_DNS
dns_lookup_family: V4_ONLY
lb_policy: ROUND_ROBIN
type: logical_dns
dns_lookup_family: v4_only
lb_policy: round_robin
load_assignment:
cluster_name: httpbin
endpoints:
@ -219,9 +226,9 @@ static_resources:
port_value: 8000
- name: nginx-backend
connect_timeout: 0.25s
type: LOGICAL_DNS
dns_lookup_family: V4_ONLY
lb_policy: ROUND_ROBIN
type: logical_dns
dns_lookup_family: v4_only
lb_policy: round_robin
load_assignment:
cluster_name: nginx-backend
endpoints:
@ -231,4 +238,15 @@ static_resources:
socket_address:
address: nginx-backend
port_value: 80
layered_runtime:
layers:
- name: static_layer_0
static_layer:
envoy:
resource_limits:
listener:
example_listener_name:
connection_limit: 10000
overload:
global_downstream_max_connections: 50000
...

View File

@ -6,7 +6,7 @@ services:
volumes:
- './example/kube:/authelia'
- './example/kube/authelia/configs/configuration.yml:/configmaps/authelia/configuration.yml'
- './common/ssl:/configmaps/authelia/ssl'
- './common/pki:/configmaps/authelia/ssl'
- './example/compose/ldap/ldif:/configmaps/ldap'
- './example/compose/nginx/backend:/configmaps/nginx-backend'
privileged: true

View File

@ -5,7 +5,7 @@ services:
image: nginx:alpine
volumes:
- ./example/compose/nginx/portal/nginx.conf:/etc/nginx/nginx.conf
- ./example/compose/nginx/portal/ssl:/etc/ssl
- ./common/pki:/pki
networks:
authelianet:
aliases:

View File

@ -16,8 +16,8 @@ http {
set $backend_endpoint https://authelia-backend:9091;
set $metrics_endpoint http://authelia-backend:9959;
ssl_certificate /etc/ssl/server.cert;
ssl_certificate_key /etc/ssl/server.key;
ssl_certificate /pki/public.bundle.crt;
ssl_certificate_key /pki/private.pem;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header X-Frame-Options "SAMEORIGIN";
@ -128,8 +128,8 @@ http {
resolver 127.0.0.11 ipv6=off;
set $upstream_endpoint http://nginx-backend;
ssl_certificate /etc/ssl/server.cert;
ssl_certificate_key /etc/ssl/server.key;
ssl_certificate /pki/public.bundle.crt;
ssl_certificate_key /pki/private.pem;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header X-Frame-Options "SAMEORIGIN";
@ -152,8 +152,8 @@ http {
set $upstream_endpoint http://nginx-backend;
set $upstream_headers http://httpbin:8000/headers;
ssl_certificate /etc/ssl/server.cert;
ssl_certificate_key /etc/ssl/server.key;
ssl_certificate /pki/public.bundle.crt;
ssl_certificate_key /pki/private.pem;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header X-Frame-Options "SAMEORIGIN";
@ -295,8 +295,8 @@ http {
set $upstream_authelia https://authelia-backend:9091/api/authz/auth-request;
set $upstream_endpoint http://oidc-client:8080;
ssl_certificate /etc/ssl/server.cert;
ssl_certificate_key /etc/ssl/server.key;
ssl_certificate /pki/public.bundle.crt;
ssl_certificate_key /pki/private.pem;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header X-Frame-Options "SAMEORIGIN";
@ -393,8 +393,8 @@ http {
resolver 127.0.0.11 ipv6=off;
set $upstream_endpoint http://smtp:1080;
ssl_certificate /etc/ssl/server.cert;
ssl_certificate_key /etc/ssl/server.key;
ssl_certificate /pki/public.bundle.crt;
ssl_certificate_key /pki/private.pem;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header X-Frame-Options "SAMEORIGIN";
@ -415,8 +415,8 @@ http {
resolver 127.0.0.11 ipv6=off;
set $upstream_endpoint http://duo-api:3000;
ssl_certificate /etc/ssl/server.cert;
ssl_certificate_key /etc/ssl/server.key;
ssl_certificate /pki/public.bundle.crt;
ssl_certificate_key /pki/private.pem;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header X-Frame-Options "SAMEORIGIN";
@ -434,8 +434,8 @@ http {
listen 8080 ssl;
server_name _;
ssl_certificate /etc/ssl/server.cert;
ssl_certificate_key /etc/ssl/server.key;
ssl_certificate /pki/public.bundle.crt;
ssl_certificate_key /pki/private.pem;
return 301 https://home.example.com:8080/;
}

View File

@ -1,19 +0,0 @@
-----BEGIN CERTIFICATE-----
MIIDEzCCAfugAwIBAgIUJZXxXExVQPJhc8TnlD+uAAYHlvwwDQYJKoZIhvcNAQEL
BQAwGDEWMBQGA1UEAwwNKi5leGFtcGxlLmNvbTAgFw0xOTA5MjYyMDAwMTBaGA8y
MTE5MDkwMjIwMDAxMFowGDEWMBQGA1UEAwwNKi5leGFtcGxlLmNvbTCCASIwDQYJ
KoZIhvcNAQEBBQADggEPADCCAQoCggEBAL3DFTAdrxG6iOj5UjSeB5lMjMQQyeYm
OxUvswwwBzmQYPUt0inAJ9QmXJ8i9Fbye8HHYUeqE5zsEfeHir81MiWfhi9oUzJt
u3bmxGLDXYaApejd18hBKITX6MYogmK2lWrl/F9zPYxc2xM/fqWnGg2xwdrMmida
hZjDUfh0rtoz8zqOzJaiiDoFMwNO+NTGmDbeOwBFYOF1OTkS3aJWwJCLZmINUG8h
Z3YPR+SL8CpGGl0xhJYAwXD1AtMlYwAteTILqrqvo2XkGsvuj0mx0w/D0DDpC48g
oSNsRIVTW3Ql3uu+kXDFtkf4I63Ctt85rZk1kX3QtYmS0pRzvmyY/b0CAwEAAaNT
MFEwHQYDVR0OBBYEFMTozK79Kp813+8TstjXRFw1MTE5MB8GA1UdIwQYMBaAFMTo
zK79Kp813+8TstjXRFw1MTE5MA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEL
BQADggEBALf1bJf3qF3m54+q98E6lSE+34yi/rVdzB9reAW1QzvvqdJRtsfjt39R
SznsbmrvCfK4SLyOj9Uhd8Z6bASPPNsUux1XAGN4AqaGmlYI8b7j3LhKCdRBZQ0I
zWgPhocyWwp5VkFe68zR06NHme/2B6eBRFsdd/69DIOv9YnEGUHk3A/9v1zvolt9
krW57Oz63zWGYXmtPPTD8of/Ya6NKqwonVx1MUQ5QzqH3WySYhRsIYqwUEXm9jt5
GEM3Nx0phEltaOLXa71nqS/Rhg/5Kod0cFaNoSKb6N93I8bqKKTK0m5wMJ5Fisrm
Pw5+AIar7RT5gHU2DD2/OTb9bXXww8I=
-----END CERTIFICATE-----

View File

@ -1,27 +0,0 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAvcMVMB2vEbqI6PlSNJ4HmUyMxBDJ5iY7FS+zDDAHOZBg9S3S
KcAn1CZcnyL0VvJ7wcdhR6oTnOwR94eKvzUyJZ+GL2hTMm27dubEYsNdhoCl6N3X
yEEohNfoxiiCYraVauX8X3M9jFzbEz9+pacaDbHB2syaJ1qFmMNR+HSu2jPzOo7M
lqKIOgUzA0741MaYNt47AEVg4XU5ORLdolbAkItmYg1QbyFndg9H5IvwKkYaXTGE
lgDBcPUC0yVjAC15Mguquq+jZeQay+6PSbHTD8PQMOkLjyChI2xEhVNbdCXe676R
cMW2R/gjrcK23zmtmTWRfdC1iZLSlHO+bJj9vQIDAQABAoIBAEZvkP/JJOCJwqPn
V3IcbmmilmV4bdi1vByDFgyiDyx4wOSA24+PubjvfFW9XcCgRPuKjDtTj/AhWBHv
B7stfa2lZuNV7/u562mZArA+IAr62Zp0LdIxDV8x3T8gbjVB3HhPYbv0RJZDKTYd
zV6jhfIrVu9mHpoY6ZnodhapCPYIyk/d49KBIHZuAc25CUjMXgTeaVtf0c996036
UxW6ef33wAOJAvW0RCvbXAJfmBeEq2qQlkjTIlpYx71fhZWexHifi8Ouv3Zonc+1
/P2Adq5uzYVBT92f9RKHg9QxxNzVrLjSMaxyvUtWQCAQfW0tFIRdqBGsHYsQrFtI
F4yzv8ECgYEA7ntpyN9HD9Z9lYQzPCR73sFCLM+ID99aVij0wHuxK97bkSyyvkLd
7MyTaym3lg1UEqWNWBCLvFULZx7F0Ah6qCzD4ymm3Bj/ADpWWPgljBI0AFml+HHs
hcATmXUrj5QbLyhiP2gmJjajp1o/rgATx6ED66seSynD6JOH8wUhhZUCgYEAy7OA
06PF8GfseNsTqlDjNF0K7lOqd21S0prdwrsJLiVzUlfMM25MLE0XLDUutCnRheeh
IlcuDoBsVTxz6rkvFGD74N+pgXlN4CicsBq5ofK060PbqCQhSII3fmHobrZ9Cr75
HmBjAxHx998SKaAAGbBbcYGUAp521i1pH5CEPYkCgYEAkUd1Zf0+2RMdZhwm6hh/
rW+l1I6IoMK70YkZsLipccRNld7Y9LbfYwYtODcts6di9AkOVfueZJiaXbONZfIE
Zrb+jkAteh9wGL9xIrnohbABJcV3Kiaco84jInUSmGDtPokncOENfHIEuEpuSJ2b
bx1TuhmAVuGWivR0+ULC7RECgYEAgS0cDRpWc9Xzh9Cl7+PLsXEvdWNpPsL9OsEq
0Ep7z9+/+f/jZtoTRCS/BTHUpDvAuwHglT5j3p5iFMt5VuiIiovWLwynGYwrbnNS
qfrIrYKUaH1n1oDS+oBZYLQGCe9/7EifAjxtjYzbvSyg//SPG7tSwfBCREbpZXj2
qSWkNsECgYA/mCDzCTlrrWPuiepo6kTmN+4TnFA+hJI6NccDVQ+jvbqEdoJ4SW4L
zqfZSZRFJMNpSgIqkQNRPJqMP0jQ5KRtJrjMWBnYxktwKz9fDg2R2MxdFgMF2LH2
HEMMhFHlv8NDjVOXh1KwRoltNGVWYsSrD9wKU9GhRCEfmNCGrvBcEg==
-----END RSA PRIVATE KEY-----

View File

@ -8,8 +8,8 @@ default_redirection_url: https://home.example.com:8080
server:
port: 443
tls:
certificate: /config/ssl/cert.pem
key: /config/ssl/key.pem
certificate: /config/ssl/public.backend.crt
key: /config/ssl/private.backend.pem
log:
level: debug

View File

@ -402,7 +402,7 @@ func (s *CLISuite) TestShouldGenerateCertificateRSAWithNotBefore() {
func (s *CLISuite) TestShouldFailGenerateCertificateRSAWithInvalidNotBefore() {
output, err := s.Exec("authelia-backend", []string{"authelia", s.testArg, s.coverageArg, "crypto", "certificate", "rsa", "generate", "--common-name=example.com", "--sans='*.example.com'", "--not-before", "Jan", "--directory=/tmp/"})
s.Assert().NotNil(err)
s.Assert().Contains(output, "Error: failed to parse not before: parsing time \"Jan\" as \"Jan 2 15:04:05 2006\": cannot parse \"\" as \"2\"")
s.Assert().Contains(output, "Error: failed to parse not before: failed to find a suitable time layout for time 'Jan'")
}
func (s *CLISuite) TestShouldGenerateCertificateRSAWith4096Bits() {
@ -555,7 +555,7 @@ func (s *CLISuite) TestShouldGenerateCertificateEd25519() {
func (s *CLISuite) TestShouldFailGenerateCertificateParseNotBefore() {
output, err := s.Exec("authelia-backend", []string{"authelia", s.testArg, s.coverageArg, "crypto", "certificate", "ecdsa", "generate", "--not-before=invalid", "--common-name=example.com", "--sans='*.example.com'", "--directory=/tmp/"})
s.Assert().NotNil(err)
s.Assert().Contains(output, "Error: failed to parse not before: parsing time \"invalid\" as \"Jan 2 15:04:05 2006\": cannot parse \"invalid\" as \"Jan\"")
s.Assert().Contains(output, "Error: failed to parse not before: failed to find a suitable time layout for time 'invalid'")
}
func (s *CLISuite) TestShouldFailGenerateCertificateECDSA() {

View File

@ -70,9 +70,22 @@ const (
)
var (
// StandardTimeLayouts is the set of standard time layouts used with ParseTimeString.
StandardTimeLayouts = []string{
"Jan 2 15:04:05 2006",
time.DateTime,
time.RFC3339,
time.RFC1123Z,
time.RubyDate,
time.ANSIC,
time.DateOnly,
}
standardDurationUnits = []string{"ns", "us", "µs", "μs", "ms", "s", "m", "h"}
reDurationSeconds = regexp.MustCompile(`^\d+$`)
reOnlyNumeric = regexp.MustCompile(`^\d+$`)
reDurationStandard = regexp.MustCompile(`(?P<Duration>[1-9]\d*?)(?P<Unit>[^\d\s]+)`)
reNumeric = regexp.MustCompile(`\d+`)
)
// Duration unit types.

View File

@ -307,7 +307,7 @@ func NewX509CertPool(directory string) (certPool *x509.CertPool, warnings []erro
}
// WriteCertificateBytesToPEM writes a certificate/csr to a file in the PEM format.
func WriteCertificateBytesToPEM(cert []byte, path string, csr bool) (err error) {
func WriteCertificateBytesToPEM(path string, csr bool, certs ...[]byte) (err error) {
out, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
if err != nil {
return fmt.Errorf("failed to open %s for writing: %w", path, err)
@ -318,11 +318,13 @@ func WriteCertificateBytesToPEM(cert []byte, path string, csr bool) (err error)
blockType = BlockTypeCertificateRequest
}
for _, cert := range certs {
if err = pem.Encode(out, &pem.Block{Bytes: cert, Type: blockType}); err != nil {
_ = out.Close()
return err
}
}
return out.Close()
}
@ -545,7 +547,7 @@ func X509ParseKeyUsage(keyUsages []string, ca bool) (keyUsage x509.KeyUsage) {
func X509ParseExtendedKeyUsage(extKeyUsages []string, ca bool) (extKeyUsage []x509.ExtKeyUsage) {
if len(extKeyUsages) == 0 {
if ca {
extKeyUsage = []x509.ExtKeyUsage{x509.ExtKeyUsageAny}
extKeyUsage = []x509.ExtKeyUsage{}
} else {
extKeyUsage = []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}
}

View File

@ -52,9 +52,9 @@ func TestShouldNotReturnErrWhenX509DirectoryExist(t *testing.T) {
}
func TestShouldReadCertsFromDirectoryButNotKeys(t *testing.T) {
pool, warnings, errors := NewX509CertPool("../suites/common/ssl/")
pool, warnings, errors := NewX509CertPool("../suites/common/pki/")
assert.NotNil(t, pool)
require.Len(t, errors, 1)
require.Len(t, errors, 2)
if runtime.GOOS == "windows" {
require.Len(t, warnings, 1)
@ -63,7 +63,8 @@ func TestShouldReadCertsFromDirectoryButNotKeys(t *testing.T) {
assert.Len(t, warnings, 0)
}
assert.EqualError(t, errors[0], "could not import certificate key.pem")
assert.EqualError(t, errors[0], "could not import certificate private.backend.pem")
assert.EqualError(t, errors[1], "could not import certificate private.pem")
}
func TestShouldGenerateCertificateAndPersistIt(t *testing.T) {
@ -441,7 +442,7 @@ func TestX509ParseExtendedKeyUsage(t *testing.T) {
expected []x509.ExtKeyUsage
}{
{"ShouldParseDefault", nil, false, []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}},
{"ShouldParseDefaultCA", nil, true, []x509.ExtKeyUsage{x509.ExtKeyUsageAny}},
{"ShouldParseDefaultCA", nil, true, []x509.ExtKeyUsage{}},
{"ShouldParseAny", [][]string{{"any"}, {"Any"}, {"any", "server_auth"}}, false, []x509.ExtKeyUsage{x509.ExtKeyUsageAny}},
{"ShouldParseServerAuth", [][]string{{"server_auth"}, {"Server_Auth"}, {"serverauth"}, {"serverAuth"}}, false, []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}},
{"ShouldParseClientAuth", [][]string{{"client_auth"}, {"Client_Auth"}, {"clientauth"}, {"clientAuth"}}, false, []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}},

View File

@ -3,6 +3,7 @@ package utils
import (
"fmt"
"strconv"
"strings"
"time"
)
@ -12,44 +13,75 @@ func StandardizeDurationString(input string) (output string, err error) {
return "0s", nil
}
matches := reDurationStandard.FindAllStringSubmatch(input, -1)
matches := reDurationStandard.FindAllStringSubmatch(strings.ReplaceAll(input, " ", ""), -1)
if len(matches) == 0 {
return "", fmt.Errorf("could not parse '%s' as a duration", input)
}
var d int
var (
o string
q int
)
for _, match := range matches {
if d, err = strconv.Atoi(match[1]); err != nil {
return "", fmt.Errorf("could not parse the numeric portion of '%s' in duration string '%s': %w", match[0], input, err)
if q, err = strconv.Atoi(match[1]); err != nil {
return "", err
}
unit := match[2]
switch {
case IsStringInSlice(unit, standardDurationUnits):
output += fmt.Sprintf("%d%s", d, unit)
case unit == DurationUnitDays:
output += fmt.Sprintf("%dh", d*HoursInDay)
case unit == DurationUnitWeeks:
output += fmt.Sprintf("%dh", d*HoursInWeek)
case unit == DurationUnitMonths:
output += fmt.Sprintf("%dh", d*HoursInMonth)
case unit == DurationUnitYears:
output += fmt.Sprintf("%dh", d*HoursInYear)
default:
return "", fmt.Errorf("could not parse the units portion of '%s' in duration string '%s': the unit '%s' is not valid", match[0], input, unit)
if o, err = standardizeQuantityAndUnits(q, match[2]); err != nil {
return "", fmt.Errorf("could not parse the units portion of '%s' in duration string '%s': %w", match[0], input, err)
}
output += o
}
return output, nil
}
func standardizeQuantityAndUnits(qty int, unit string) (output string, err error) {
switch {
case IsStringInSlice(unit, standardDurationUnits):
return fmt.Sprintf("%d%s", qty, unit), nil
case len(unit) == 1:
switch unit {
case DurationUnitDays:
return fmt.Sprintf("%dh", qty*HoursInDay), nil
case DurationUnitWeeks:
return fmt.Sprintf("%dh", qty*HoursInWeek), nil
case DurationUnitMonths:
return fmt.Sprintf("%dh", qty*HoursInMonth), nil
case DurationUnitYears:
return fmt.Sprintf("%dh", qty*HoursInYear), nil
}
default:
switch unit {
case "millisecond", "milliseconds":
return fmt.Sprintf("%dms", qty), nil
case "second", "seconds":
return fmt.Sprintf("%ds", qty), nil
case "minute", "minutes":
return fmt.Sprintf("%dm", qty), nil
case "hour", "hours":
return fmt.Sprintf("%dh", qty), nil
case "day", "days":
return fmt.Sprintf("%dh", qty*HoursInDay), nil
case "week", "weeks":
return fmt.Sprintf("%dh", qty*HoursInWeek), nil
case "month", "months":
return fmt.Sprintf("%dh", qty*HoursInMonth), nil
case "year", "years":
return fmt.Sprintf("%dh", qty*HoursInYear), nil
}
}
return "", fmt.Errorf("the unit '%s' is not valid", unit)
}
// ParseDurationString standardizes a duration string with StandardizeDurationString then uses time.ParseDuration to
// convert it into a time.Duration.
func ParseDurationString(input string) (duration time.Duration, err error) {
if reDurationSeconds.MatchString(input) {
if reOnlyNumeric.MatchString(input) {
var seconds int
if seconds, err = strconv.Atoi(input); err != nil {
@ -68,6 +100,50 @@ func ParseDurationString(input string) (duration time.Duration, err error) {
return time.ParseDuration(out)
}
// ParseTimeString attempts to parse a string with several time formats.
func ParseTimeString(input string) (t time.Time, err error) {
return ParseTimeStringWithLayouts(input, StandardTimeLayouts)
}
// ParseTimeStringWithLayouts attempts to parse a string with several time formats. The format with the most matching
// characters is returned.
func ParseTimeStringWithLayouts(input string, layouts []string) (match time.Time, err error) {
_, match, err = matchParseTimeStringWithLayouts(input, layouts)
return
}
func matchParseTimeStringWithLayouts(input string, layouts []string) (index int, match time.Time, err error) {
if reOnlyNumeric.MatchString(input) {
var u int64
if u, err = strconv.ParseInt(input, 10, 64); err != nil {
return -999, match, fmt.Errorf("time value was detected as an integer but the integer could not be parsed: %w", err)
}
switch {
case u > 32503554000000: // 2999-12-31 00:00:00 in unix time (milliseconds).
return -3, time.UnixMicro(u), nil
case u > 946645200000: // 2000-01-01 00:00:00 in unix time (milliseconds).
return -2, time.UnixMilli(u), nil
default:
return -1, time.Unix(u, 0), nil
}
}
var layout string
for index, layout = range layouts {
if match, err = time.Parse(layout, input); err == nil {
if len(match.Format(layout))-len(input) == 0 {
return index, match, nil
}
}
}
return -998, time.UnixMilli(0), fmt.Errorf("failed to find a suitable time layout for time '%s'", input)
}
// UnixNanoTimeToMicrosoftNTEpoch converts a unix timestamp in nanosecond format to win32 epoch format.
func UnixNanoTimeToMicrosoftNTEpoch(nano int64) (t uint64) {
return uint64(nano/100) + timeUnixEpochAsMicrosoftNTEpoch

View File

@ -1,63 +1,112 @@
package utils
import (
"fmt"
"strings"
"testing"
"time"
"github.com/stretchr/testify/assert"
)
func TestParseDurationString_ShouldParseDurationString(t *testing.T) {
duration, err := ParseDurationString("1h")
func TestParseDurationString(t *testing.T) {
testCases := []struct {
name string
have []string
raw bool
expected time.Duration
err string
}{
{"ShouldParseStringsForMillisecond", []string{"%d ms", "%d millisecond", "%d milliseconds"}, false, time.Millisecond, ""},
{"ShouldParseStringsForSecond", []string{"%d s", "%d second", "%d seconds"}, false, time.Second, ""},
{"ShouldParseStringsForMinute", []string{"%d m", "%d minute", "%d minutes"}, false, time.Minute, ""},
{"ShouldParseStringsForHour", []string{"%d h", "%d hour", "%d hours"}, false, time.Hour, ""},
{"ShouldParseStringsForDay", []string{"%d d", "%d day", "%d days"}, false, time.Hour * HoursInDay, ""},
{"ShouldParseStringsForWeek", []string{"%d w", "%d week", "%d weeks"}, false, time.Hour * HoursInWeek, ""},
{"ShouldParseStringsForMonth", []string{"%d M", "%d month", "%d months"}, false, time.Hour * HoursInMonth, ""},
{"ShouldParseStringsForYear", []string{"%d y", "%d year", "%d years"}, false, time.Hour * HoursInYear, ""},
{"ShouldParseStringsDecimals", []string{"100"}, true, time.Second * 100, ""},
{"ShouldParseStringsDecimalNull", []string{""}, true, time.Second * 0, ""},
}
assert.NoError(t, err)
assert.Equal(t, 60*time.Minute, duration)
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
for _, f := range tc.have {
if tc.raw {
t.Run(f, func(t *testing.T) {
actual, actualErr := ParseDurationString(f)
func TestParseDurationString_ShouldParseBlankString(t *testing.T) {
duration, err := ParseDurationString("")
if tc.err == "" {
assert.NoError(t, actualErr)
assert.Equal(t, tc.expected, actual)
} else {
assert.EqualError(t, actualErr, tc.err)
}
})
} else {
for _, d := range []int{1, 5, 20} {
input := fmt.Sprintf(f, d)
assert.NoError(t, err)
assert.Equal(t, time.Second*0, duration)
}
inputNoSpace := strings.ReplaceAll(input, " ", "")
func TestParseDurationString_ShouldParseDurationStringAllUnits(t *testing.T) {
duration, err := ParseDurationString("1y")
t.Run(inputNoSpace, func(t *testing.T) {
t.Run("WithSpaces", func(t *testing.T) {
actual, actualErr := ParseDurationString(input)
assert.NoError(t, err)
assert.Equal(t, time.Hour*24*365, duration)
if tc.err == "" {
assert.NoError(t, actualErr)
assert.Equal(t, tc.expected*time.Duration(d), actual)
} else {
assert.EqualError(t, actualErr, tc.err)
}
duration, err = ParseDurationString("1M")
t.Run("LeadingZeros", func(t *testing.T) {
inputActual := reNumeric.ReplaceAllStringFunc(input, func(s string) string {
return "000" + s
})
assert.NoError(t, err)
assert.Equal(t, time.Hour*24*30, duration)
actual, actualErr := ParseDurationString(inputActual)
duration, err = ParseDurationString("1w")
if tc.err == "" {
assert.NoError(t, actualErr)
assert.Equal(t, tc.expected*time.Duration(d), actual)
} else {
assert.EqualError(t, actualErr, tc.err)
}
})
})
assert.NoError(t, err)
assert.Equal(t, time.Hour*24*7, duration)
t.Run("WithoutSpaces", func(t *testing.T) {
actual, actualErr := ParseDurationString(inputNoSpace)
duration, err = ParseDurationString("1d")
if tc.err == "" {
assert.NoError(t, actualErr)
assert.Equal(t, tc.expected*time.Duration(d), actual)
} else {
assert.EqualError(t, actualErr, tc.err)
}
assert.NoError(t, err)
assert.Equal(t, time.Hour*24, duration)
t.Run("LeadingZeros", func(t *testing.T) {
inputActual := reNumeric.ReplaceAllStringFunc(inputNoSpace, func(s string) string {
return "000" + s
})
duration, err = ParseDurationString("1h")
actual, actualErr := ParseDurationString(inputActual)
assert.NoError(t, err)
assert.Equal(t, time.Hour, duration)
duration, err = ParseDurationString("1s")
assert.NoError(t, err)
assert.Equal(t, time.Second, duration)
}
func TestParseDurationString_ShouldParseSecondsString(t *testing.T) {
duration, err := ParseDurationString("100")
assert.NoError(t, err)
assert.Equal(t, 100*time.Second, duration)
if tc.err == "" {
assert.NoError(t, actualErr)
assert.Equal(t, tc.expected*time.Duration(d), actual)
} else {
assert.EqualError(t, actualErr, tc.err)
}
})
})
})
}
}
}
})
}
}
func TestParseDurationString_ShouldNotParseDurationStringWithOutOfOrderQuantitiesAndUnits(t *testing.T) {
@ -74,18 +123,11 @@ func TestParseDurationString_ShouldNotParseBadDurationString(t *testing.T) {
assert.Equal(t, time.Duration(0), duration)
}
func TestParseDurationString_ShouldParseDurationStringWithMultiValueUnits(t *testing.T) {
duration, err := ParseDurationString("10ms")
func TestParseDurationString_ShouldNotParseBadDurationStringAlt(t *testing.T) {
duration, err := ParseDurationString("10abcxyz")
assert.NoError(t, err)
assert.Equal(t, time.Duration(10)*time.Millisecond, duration)
}
func TestParseDurationString_ShouldParseDurationStringWithLeadingZero(t *testing.T) {
duration, err := ParseDurationString("005h")
assert.NoError(t, err)
assert.Equal(t, time.Duration(5)*time.Hour, duration)
assert.EqualError(t, err, "could not parse the units portion of '10abcxyz' in duration string '10abcxyz': the unit 'abcxyz' is not valid")
assert.Equal(t, time.Duration(0), duration)
}
func TestParseDurationString_ShouldParseMultiUnitValues(t *testing.T) {
@ -130,3 +172,35 @@ func TestShouldConvertKnownUnixNanoTimeToKnownWin32Epoch(t *testing.T) {
assert.Equal(t, win32Epoch, UnixNanoTimeToMicrosoftNTEpoch(exampleNanoTime))
assert.Equal(t, timeUnixEpochAsMicrosoftNTEpoch, UnixNanoTimeToMicrosoftNTEpoch(0))
}
func TestParseTimeString(t *testing.T) {
testCases := []struct {
name string
have string
index int
expected time.Time
err string
}{
{"ShouldParseIntegerAsUnix", "1675899060", -1, time.Unix(1675899060, 0), ""},
{"ShouldParseIntegerAsUnixMilli", "1675899060000", -2, time.Unix(1675899060, 0), ""},
{"ShouldParseIntegerAsUnixMicro", "1675899060000000", -3, time.Unix(1675899060, 0), ""},
{"ShouldNotParseSuperLargeInteger", "9999999999999999999999999999999999999999", -999, time.Unix(0, 0), "time value was detected as an integer but the integer could not be parsed: strconv.ParseInt: parsing \"9999999999999999999999999999999999999999\": value out of range"},
{"ShouldParseSimpleTime", "Jan 2 15:04:05 2006", 0, time.Unix(1136214245, 0), ""},
{"ShouldNotParseInvalidTime", "abc", -998, time.Unix(0, 0), "failed to find a suitable time layout for time 'abc'"},
{"ShouldMatchDate", "2020-05-01", 6, time.Unix(1588291200, 0), ""},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
index, actual, err := matchParseTimeStringWithLayouts(tc.have, StandardTimeLayouts)
if tc.err == "" {
assert.NoError(t, err)
assert.Equal(t, tc.index, index)
assert.Equal(t, tc.expected.UnixNano(), actual.UnixNano())
} else {
assert.EqualError(t, err, tc.err)
}
})
}
}