integration: add single-cluster integration tests (#2516)

* integration: add single-cluster integration tests

* remove kind load
This commit is contained in:
Caleb Doxsey 2021-08-24 15:35:05 -06:00 committed by GitHub
parent f5a558d4a0
commit 48cd10d46b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
53 changed files with 7455 additions and 31 deletions

View file

@ -0,0 +1,62 @@
local utils = import '../utils.libsonnet';
function() {
local name = 'fortio',
local image = 'fortio/fortio:1.17.0',
compose: {
services:
utils.ComposeService(name, {
image: image,
depends_on: {
[name + '-init']: {
condition: 'service_completed_successfully',
},
},
command: [
'server',
'-cert',
'/fortio_config/trusted.pem',
'-key',
'/fortio_config/trusted-key.pem',
],
ports: [
'8079:8079/tcp',
],
volumes: [
'fortio_config:/fortio_config',
],
}) +
utils.ComposeService(name + '-init', {
image: 'busybox:latest',
command: [
'sh',
'-c',
|||
echo "$$CERT" >/fortio_config/trusted.pem
echo "$$KEY" >/fortio_config/trusted-key.pem
|||,
],
environment: {
CERT: importstr '../files/trusted.pem',
KEY: importstr '../files/trusted-key.pem',
},
volumes: [
'fortio_config:/fortio_config',
],
}) +
utils.ComposeService(name + '-ready', {
image: 'jwilder/dockerize:0.6.1',
command: [
'-wait',
'http://' + name + ':8080',
'-timeout',
'10m',
],
}),
volumes: {
fortio_config: {},
},
},
kubernetes: [],
}

View file

@ -0,0 +1,76 @@
local utils = import '../utils.libsonnet';
local Variations() =
[
{
name: 'trusted',
cert: importstr '../files/trusted.pem',
key: importstr '../files/trusted-key.pem',
},
{
name: 'untrusted',
cert: importstr '../files/untrusted.pem',
key: importstr '../files/untrusted-key.pem',
},
{
name: 'wrongly-named',
cert: importstr '../files/invalid.pem',
key: importstr '../files/invalid-key.pem',
},
];
local Command(variation) =
[
'sh',
'-c',
|||
cat <<-END_OF_HTTPDETAILS | tee /app/fullchain.pem
%s
END_OF_HTTPDETAILS
cat <<-END_OF_HTTPDETAILS | tee /app/privkey.pem
%s
END_OF_HTTPDETAILS
node ./index.js
||| % [variation.cert, variation.key],
];
function() {
local suffix = 'httpdetails',
local image = 'mendhak/http-https-echo:19',
compose: {
services: std.foldl(
function(acc, variation)
acc +
utils.ComposeService(variation.name + '-' + suffix, {
image: image,
command: Command(variation),
}) +
utils.ComposeService(variation.name + '-' + suffix + '-ready', {
image: 'jwilder/dockerize:0.6.1',
command: [
'-wait',
'http://' + variation.name + '-' + suffix + ':8080',
'-timeout',
'10m',
],
}),
Variations(),
{}
),
},
kubernetes: std.foldl(
function(acc, variation)
acc + [
utils.KubernetesDeployment(variation.name + '-' + suffix, image, Command(variation), [
{ name: 'http', containerPort: 8080 },
{ name: 'https', containerPort: 8443 },
]),
utils.KubernetesService(variation.name + '-' + suffix, [
{ name: 'http', port: 8080, targetPort: 'http' },
{ name: 'https', port: 8443, targetPort: 'https' },
]),
], Variations(), []
),
}

View file

@ -0,0 +1,43 @@
local utils = import '../utils.libsonnet';
function(idp) {
local name = 'mock-idp',
local image = 'pomerium/mock-idps:${MOCK_IDPS_TAG:-master}',
local command = [
'--provider',
idp,
'--port',
'8024',
'--root-url',
'https://mock-idp.localhost.pomerium.io/',
],
compose: {
services:
utils.ComposeService(name, {
image: image,
command: command,
ports: [
'8024:8024/tcp',
],
}) +
utils.ComposeService(name + '-ready', {
image: 'jwilder/dockerize:0.6.1',
command: [
'-wait',
'http://' + name + ':8024/.well-known/openid-configuration',
'-timeout',
'10m',
],
}),
volumes: {},
},
kubernetes: [
utils.KubernetesDeployment(name, image, command, [
{ name: 'http', containerPort: 8024 },
]),
utils.KubernetesService(name, [
{ name: 'http', port: 8024, targetPort: 'http' },
]),
],
}

View file

@ -0,0 +1,215 @@
local utils = import '../utils.libsonnet';
local Routes = (import './routes.libsonnet').Routes;
local GoogleCloudServerlessAuthenticationServiceAccount(dns_suffix='') =
{
type: 'service_account',
project_id: 'pomerium-redacted',
private_key_id: 'e07f7c93870c7e03f883560ecd8fd0f4d27b0081',
private_key: importstr '../files/trusted-key.pem',
client_email: 'redacted@pomerium-redacted.iam.gserviceaccount.com',
client_id: '101215990458000334387',
auth_uri: 'http://mock-idp' + dns_suffix + ':8024',
token_uri: 'http://mock-idp' + dns_suffix + ':8024/token',
auth_provider_x509_cert_url: 'http://mock-idp' + dns_suffix + ':8024',
client_x509_cert_url: 'http://mock-idp' + dns_suffix + ':8024',
};
local KubernetesDeployment(name, image, environment) =
{
apiVersion: 'apps/v1',
kind: 'Deployment',
metadata: {
namespace: 'default',
name: name,
},
spec: {
replicas: 1,
selector: { matchLabels: { app: name } },
template: {
metadata: {
labels: { app: name },
},
spec: {
containers: [{
name: name,
image: image,
imagePullPolicy: 'IfNotPresent',
ports: [
{ name: 'http', containerPort: 80 },
{ name: 'https', containerPort: 443 },
{ name: 'grpc', containerPort: 5443 },
],
env: [
{
name: k,
value: environment[k],
}
for k in std.objectFields(environment)
],
}],
},
},
},
};
local KubernetesService(name) =
{
apiVersion: 'v1',
kind: 'Service',
metadata: {
namespace: 'default',
name: name,
labels: { app: name },
},
spec: {
type: 'NodePort',
selector: { app: name },
ports: [
{ name: 'http', port: 80, targetPort: 'http', nodePort: 80 },
{ name: 'https', port: 443, targetPort: 'https', nodePort: 443 },
{ name: 'grpc', port: 5443, targetPort: 'grpc', nodePort: 5443 },
],
},
};
local Environment(mode, idp, dns_suffix) =
{
AUTHENTICATE_SERVICE_URL: 'https://authenticate.localhost.pomerium.io',
CERTIFICATE: std.base64(importstr '../files/trusted.pem'),
CERTIFICATE_KEY: std.base64(importstr '../files/trusted-key.pem'),
CERTIFICATE_AUTHORITY: std.base64(importstr '../files/ca.pem'),
COOKIE_SECRET: 'UYgnt8bxxK5G2sFaNzyqi5Z+OgF8m2akNc0xdQx718w=',
DATABROKER_STORAGE_TYPE: 'redis',
DATABROKER_STORAGE_CONNECTION_STRING: 'redis://redis:6379',
ENVOY_ADMIN_ADDRESS: '0.0.0.0:9901',
GOOGLE_CLOUD_SERVERLESS_AUTHENTICATION_SERVICE_ACCOUNT: std.base64(std.manifestJsonEx(
GoogleCloudServerlessAuthenticationServiceAccount(dns_suffix), ''
)),
IDP_PROVIDER: idp,
IDP_PROVIDER_URL: 'https://mock-idp.localhost.pomerium.io/',
IDP_CLIENT_ID: 'CLIENT_ID',
IDP_CLIENT_SECRET: 'CLIENT_SECRET',
JWT_CLAIMS_HEADERS: 'email,groups,user',
LOG_LEVEL: 'info',
POLICY: std.base64(std.manifestJsonEx(Routes(mode, idp, dns_suffix), '')),
SHARED_SECRET: 'UYgnt8bxxK5G2sFaNzyqi5Z+OgF8m2akNc0xdQx718w=',
SIGNING_KEY: std.base64(importstr '../files/signing-key.pem'),
SIGNING_KEY_ALGORITHM: 'ES256',
} + if mode == 'multi' then {
AUTHORIZE_SERVICE_URL: 'https://pomerium-authorize:5443',
DATABROKER_SERVICE_URL: 'https://pomerium-databroker:5443',
GRPC_ADDRESS: ':5443',
GRPC_INSECURE: 'false',
OVERRIDE_CERTIFICATE_NAME: '*.localhost.pomerium.io',
} else if mode == 'traefik' then {
FORWARD_AUTH_URL: 'https://forward-authenticate.localhost.pomerium.io',
} else if mode == 'nginx' then {
ADDRESS: ':80',
INSECURE_SERVER: 'true',
FORWARD_AUTH_URL: 'https://forward-authenticate.localhost.pomerium.io',
} else {};
local ComposeService(name, definition, additionalAliases=[]) =
utils.ComposeService(name, definition {
depends_on: {
[name + '-ready']: {
condition: 'service_completed_successfully',
}
for name in [
'fortio',
'mock-idp',
'redis',
'trusted-httpdetails',
'untrusted-httpdetails',
'verify',
'websocket-echo',
'wrongly-named-httpdetails',
]
},
}, additionalAliases);
function(mode, idp, dns_suffix='') {
local name = 'pomerium',
local image = 'pomerium/pomerium:${POMERIUM_TAG:-master}',
local environment = Environment(mode, idp, dns_suffix),
compose: {
services: if mode == 'multi' then
ComposeService(name + '-authorize', {
image: image,
environment: environment {
SERVICES: 'authorize',
},
ports: [
'9904:9901/tcp',
'5446:5443/tcp',
],
}) +
ComposeService(name + '-authenticate', {
image: image,
environment: environment {
SERVICES: 'authenticate',
},
ports: [
'9903:9901/tcp',
'5445:5443/tcp',
],
}, ['authenticate.localhost.pomerium.io']) +
ComposeService(name + '-databroker', {
image: image,
environment: environment {
SERVICES: 'databroker',
},
ports: [
'9902:9901/tcp',
'5444:5443/tcp',
],
}) +
ComposeService(name + '-proxy', {
image: image,
environment: environment {
SERVICES: 'proxy',
},
ports: [
'80:80/tcp',
'443:443/tcp',
'5443:5443/tcp',
'9901:9901/tcp',
],
}, ['mock-idp.localhost.pomerium.io'])
else if mode == 'traefik' || mode == 'nginx' then
ComposeService(name, {
image: image,
environment: environment,
}, ['authenticate.localhost.pomerium.io', 'forward-authenticate.localhost.pomerium.io']) +
ComposeService(name + '-ready', {
image: 'jwilder/dockerize:0.6.1',
command: [
'-wait',
if mode == 'nginx' then
'http://' + name + ':80/healthz'
else
'https://' + name + ':443/healthz',
'-timeout',
'10m',
],
})
else
ComposeService(name, {
image: image,
environment: environment,
ports: [
'80:80/tcp',
'443:443/tcp',
'9901:9901/tcp',
],
}, ['authenticate.localhost.pomerium.io']),
volumes: {},
},
kubernetes: [
KubernetesService(name),
KubernetesDeployment(name, image, environment),
],
}

View file

@ -0,0 +1,30 @@
local utils = import '../utils.libsonnet';
function() {
local name = 'redis',
local image = 'redis:6.2.5-alpine',
compose: {
services:
utils.ComposeService(name, {
image: image,
}) +
utils.ComposeService(name + '-ready', {
image: 'jwilder/dockerize:0.6.1',
command: [
'-wait',
'tcp://' + name + ':6379',
'-timeout',
'10m',
],
}),
},
kubernetes: [
utils.KubernetesDeployment(name, image, null, [
{ name: 'tcp', containerPort: 6379 },
]),
utils.KubernetesService(name, [
{ name: 'tcp', port: 6379, targetPort: 'tcp' },
]),
],
}

View file

@ -0,0 +1,197 @@
local Routes(mode, idp, dns_suffix) =
[
{
from: 'https://mock-idp.localhost.pomerium.io',
to: 'http://mock-idp' + dns_suffix + ':8024',
allow_public_unauthenticated_access: true,
preserve_host_header: true,
},
{
from: 'https://envoy.localhost.pomerium.io',
to: 'http://localhost:9901',
allow_public_unauthenticated_access: true,
},
{
from: 'https://verify.localhost.pomerium.io',
to: 'http://verify' + dns_suffix + ':80',
allow_any_authenticated_user: true,
pass_identity_headers: true,
},
{
from: 'https://websocket-echo.localhost.pomerium.io',
to: 'http://websocket-echo' + dns_suffix + ':80',
allow_public_unauthenticated_access: true,
allow_websockets: true,
},
{
from: 'https://fortio-ui.localhost.pomerium.io',
to: 'https://fortio' + dns_suffix + ':8080',
allow_any_authenticated_user: true,
},
{
from: 'https://fortio-ping.localhost.pomerium.io',
to: 'https://fortio' + dns_suffix + ':8079',
allow_public_unauthenticated_access: true,
tls_custom_ca: std.base64(importstr '../files/ca.pem'),
tls_server_name: 'fortio-ping.localhost.pomerium.io',
},
{
from: 'tcp+https://redis.localhost.pomerium.io:6379',
to: 'tcp://redis' + dns_suffix + ':6379',
allow_any_authenticated_user: true,
},
// tls_skip_verify
{
from: 'https://httpdetails.localhost.pomerium.io',
to: 'https://trusted-httpdetails' + dns_suffix + ':8443',
path: '/tls-skip-verify-enabled',
tls_skip_verify: true,
allow_public_unauthenticated_access: true,
},
{
from: 'https://httpdetails.localhost.pomerium.io',
to: 'https://trusted-httpdetails' + dns_suffix + ':8443',
path: '/tls-skip-verify-disabled',
tls_skip_verify: false,
allow_public_unauthenticated_access: true,
},
// tls_server_name
{
from: 'https://httpdetails.localhost.pomerium.io',
to: 'https://wrongly-named-httpdetails' + dns_suffix + ':8443',
path: '/tls-server-name-enabled',
tls_server_name: 'httpdetails.localhost.notpomerium.io',
allow_public_unauthenticated_access: true,
},
{
from: 'https://httpdetails.localhost.pomerium.io',
to: 'https://wrongly-named-httpdetails' + dns_suffix + ':8443',
path: '/tls-server-name-disabled',
allow_public_unauthenticated_access: true,
},
// tls_custom_certificate_authority
{
from: 'https://httpdetails.localhost.pomerium.io',
to: 'https://untrusted-httpdetails' + dns_suffix + ':8443',
path: '/tls-custom-ca-enabled',
tls_custom_ca: std.base64(importstr '../files/untrusted-ca.pem'),
tls_server_name: 'httpdetails.localhost.pomerium.io',
allow_public_unauthenticated_access: true,
},
{
from: 'https://httpdetails.localhost.pomerium.io',
to: 'https://untrusted-httpdetails' + dns_suffix + ':8443',
path: '/tls-custom-ca-disabled',
allow_public_unauthenticated_access: true,
},
// tls_client_cert
// {
// from: 'http://httpdetails.localhost.pomerium.io',
// to: 'https://mtls-http-details' + dns_suffix + ':8443',
// path: '/tls-client-cert-enabled',
// tls_client_cert: std.base64(tls.trusted.client.cert),
// tls_client_key: std.base64(tls.trusted.client.key),
// tls_server_name: 'httpdetails.localhost.pomerium.io',
// allow_public_unauthenticated_access: true,
// },
// {
// from: 'http://httpdetails.localhost.pomerium.io',
// to: 'https://mtls-http-details' + dns_suffix + ':8443',
// path: '/tls-client-cert-disabled',
// allow_public_unauthenticated_access: true,
// },
// cors_allow_preflight option
{
from: 'https://httpdetails.localhost.pomerium.io',
to: 'http://trusted-httpdetails' + dns_suffix + ':8080',
prefix: '/cors-enabled',
cors_allow_preflight: true,
},
{
from: 'https://httpdetails.localhost.pomerium.io',
to: 'http://trusted-httpdetails' + dns_suffix + ':8080',
prefix: '/cors-disabled',
cors_allow_preflight: false,
},
// preserve_host_header option
{
from: 'https://httpdetails.localhost.pomerium.io',
to: 'http://trusted-httpdetails' + dns_suffix + ':8080',
prefix: '/preserve-host-header-enabled',
allow_public_unauthenticated_access: true,
preserve_host_header: true,
},
{
from: 'https://httpdetails.localhost.pomerium.io',
to: 'http://trusted-httpdetails' + dns_suffix + ':8080',
prefix: '/preserve-host-header-disabled',
allow_public_unauthenticated_access: true,
preserve_host_header: false,
},
// authorization policy
{
from: 'https://restricted-httpdetails.localhost.pomerium.io',
to: 'http://trusted-httpdetails' + dns_suffix + ':8080',
allow_any_authenticated_user: true,
pass_identity_headers: true,
},
{
from: 'https://httpdetails.localhost.pomerium.io',
prefix: '/by-domain',
to: 'http://trusted-httpdetails' + dns_suffix + ':8080',
allowed_domains: ['dogs.test'],
pass_identity_headers: true,
},
{
from: 'https://httpdetails.localhost.pomerium.io',
prefix: '/by-user',
to: 'http://trusted-httpdetails' + dns_suffix + ':8080',
allowed_users: ['user1@dogs.test'],
pass_identity_headers: true,
},
// catch-all
{
from: 'https://httpdetails.localhost.pomerium.io',
to: 'http://trusted-httpdetails' + dns_suffix + ':8080',
allow_public_unauthenticated_access: true,
pass_identity_headers: true,
set_request_headers: {
'X-Custom-Request-Header': 'custom-request-header-value',
},
},
// websockets
{
from: 'https://enabled-ws-echo.localhost.pomerium.io',
to: 'http://websocket-echo' + dns_suffix + ':80',
allow_public_unauthenticated_access: true,
allow_websockets: true,
},
{
from: 'https://disabled-ws-echo.localhost.pomerium.io',
to: 'http://websocket-echo' + dns_suffix + ':80',
allow_public_unauthenticated_access: true,
},
// cloudrun
{
from: 'https://cloudrun.localhost.pomerium.io',
to: 'http://trusted-httpdetails' + dns_suffix + ':8080',
allow_public_unauthenticated_access: true,
pass_identity_headers: true,
enable_google_cloud_serverless_authentication: true,
set_request_headers: {
'x-idp': idp,
},
},
] + if mode == 'multi' then [
{
from: 'https://authenticate.localhost.pomerium.io',
to: 'https://pomerium-authenticate',
allow_public_unauthenticated_access: true,
host_rewrite: 'authenticate.localhost.pomerium.io',
tls_skip_verify: true,
},
] else [];
{
Routes: Routes,
}

View file

@ -0,0 +1,55 @@
local utils = import '../utils.libsonnet';
function(mode) {
local name = 'verify',
local image = 'pomerium/verify:${VERIFY_TAG:-latest}',
compose: {
services:
utils.ComposeService(name, {
image: image,
depends_on: {
[name + '-init']: {
condition: 'service_completed_successfully',
},
},
environment: {
SSL_CERT_FILE: '/verify_config/ca.pem',
},
volumes: [
'verify_config:/verify_config',
],
}) +
utils.ComposeService(name + '-init', {
image: 'busybox:latest',
command: [
'sh',
'-c',
"echo '" + (importstr '../files/ca.pem') + "' > /verify_config/ca.pem",
],
volumes: [
'verify_config:/verify_config',
],
}) +
utils.ComposeService(name + '-ready', {
image: 'jwilder/dockerize:0.6.1',
command: [
'-wait',
'http://' + name + ':80/',
'-timeout',
'10m',
],
}),
volumes: {
verify_config: {},
},
},
kubernetes: [
utils.KubernetesService(name, [
{ name: 'http', port: 80, targetPort: 'http' },
]),
utils.KubernetesDeployment(name, image, null, [
{ name: 'http', containerPort: 80 },
]),
],
}

View file

@ -0,0 +1,33 @@
local utils = import '../utils.libsonnet';
function() {
local name = 'websocket-echo',
local image = 'pvtmert/websocketd:latest',
local command = ['--port', '80', 'tee'],
compose: {
services:
utils.ComposeService(name, {
image: image,
command: command,
}) +
utils.ComposeService(name + '-ready', {
image: 'jwilder/dockerize:0.6.1',
command: [
'-wait',
'tcp://' + name + ':80',
'-timeout',
'10m',
],
}),
volumes: {},
},
kubernetes: [
utils.KubernetesDeployment(name, image, command, [
{ name: 'http', containerPort: 80 },
]),
utils.KubernetesService(name, [
{ name: 'http', port: 80, targetPort: 'http' },
]),
],
}

View file

@ -0,0 +1 @@
(import '../../deployments/single.libsonnet')('auth0')

View file

@ -0,0 +1 @@
(import '../../deployments/single.libsonnet')('azure')

View file

@ -0,0 +1 @@
(import '../../deployments/single.libsonnet')('github')

View file

@ -0,0 +1 @@
(import '../../deployments/single.libsonnet')('gitlab')

View file

@ -0,0 +1 @@
(import '../../deployments/single.libsonnet')('google')

View file

@ -0,0 +1 @@
(import '../../deployments/single.libsonnet')('oidc')

View file

@ -0,0 +1 @@
(import '../../deployments/single.libsonnet')('okta')

View file

@ -0,0 +1 @@
(import '../../deployments/single.libsonnet')('onelogin')

View file

@ -0,0 +1 @@
(import '../../deployments/single.libsonnet')('ping')

View file

@ -0,0 +1,16 @@
local utils = import '../utils.libsonnet';
function(idp) utils.Merge([
(import '../backends/fortio.libsonnet')().compose,
(import '../backends/httpdetails.libsonnet')().compose,
(import '../backends/mock-idp.libsonnet')(idp).compose,
(import '../backends/pomerium.libsonnet')('single', idp).compose,
(import '../backends/redis.libsonnet')().compose,
(import '../backends/verify.libsonnet')('single').compose,
(import '../backends/websocket-echo.libsonnet')().compose,
{
networks: {
main: {},
},
},
])

View file

@ -0,0 +1,40 @@
-----BEGIN PRIVATE KEY-----
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
JO5gS4pQR3MBP6W0qDfF/mb0NA==
-----END PRIVATE KEY-----

View file

@ -0,0 +1,28 @@
-----BEGIN CERTIFICATE-----
MIIE1zCCAz+gAwIBAgIQZ139cd/paPdkS2JyAu7kEDANBgkqhkiG9w0BAQsFADCB
gzEeMBwGA1UEChMVbWtjZXJ0IGRldmVsb3BtZW50IENBMSwwKgYDVQQLDCNjYWxl
YkBjYWxlYi1wYy1saW51eCAoQ2FsZWIgRG94c2V5KTEzMDEGA1UEAwwqbWtjZXJ0
IGNhbGViQGNhbGViLXBjLWxpbnV4IChDYWxlYiBEb3hzZXkpMB4XDTIxMDgxMDE3
MzIwOVoXDTMxMDgxMDE3MzIwOVowgYMxHjAcBgNVBAoTFW1rY2VydCBkZXZlbG9w
bWVudCBDQTEsMCoGA1UECwwjY2FsZWJAY2FsZWItcGMtbGludXggKENhbGViIERv
eHNleSkxMzAxBgNVBAMMKm1rY2VydCBjYWxlYkBjYWxlYi1wYy1saW51eCAoQ2Fs
ZWIgRG94c2V5KTCCAaIwDQYJKoZIhvcNAQEBBQADggGPADCCAYoCggGBANbKyMz5
MVW6YKdjh1oIN1Mn7PE2pH5SbJSpWxdAGhdBkBkpAa7OxarjH5KVkCTSa7oncla7
qNuJZS6mBmoxF+R+cR3jyGdUAYlozl1jlfqLIfC/+g7V7VmOJn98tjB42fatxLl6
WPAw1JDNsWtQfhKhbcHut7RsF0rMOOHcwywTR7LOyCmIel1pcmpV4hbVcT6eVwoP
HXyJSa9cqaMQ5Xrdogai4IqZZIGLHeLsTVutOgJFXEevlX/QT3sWomEctzh38Js4
9DiAPD6d4Y7/CPLYEfk29JQ9NZhpgDsi9hu5FHHZcXwf1IHlw/CBVgn6j+jmvKKz
90Ma1oquv3W6dttid/xCcLGu2S+96Tzrykmoy5VacLtVEP41YmoVls91rlo7olpe
QWFbnmco739TI/4h+HodolperQERQl7uCnpKVPZ3WokKuRh5pkqkQp/arQjtwcRt
G43CrDpbl+uSjMCAxha958eTYvtojTMnvLtsGID1hGXnqlw+5KjKrgRHrQIDAQAB
o0UwQzAOBgNVHQ8BAf8EBAMCAgQwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4E
FgQUhYZYWIBHyk6ZVTnp3lRt/tyBP00wDQYJKoZIhvcNAQELBQADggGBAA1F/apr
l6pNT3Mp/MxhUUgo6usEJCryGQcLRfexyQXGN3huCmIrP55VFa8ETPAtjsr6PMe7
7vvEj8eFu2JtKovlQwNewYU9cjAMCVaFiNbrQa20hzhWc2js6dyildE6/DPzbeds
KDAxhFNp35SlwtRtKk1SzxJxsqSwjfxI8fp+R/0wO8g0fWTdM2gCpRwYMNwJELEg
+dSlvJCwuu+rzxLalzaPF1PMTW72OELal/j5sD+2VytQ4k+HUDbyt2DnQT7YQ3zo
q02x2u2sm1WW/o/uh8pjPxkGQqL2mryZs6VH9VCU3QkKNDssNd71lr3wPoE4YRHe
UvzD1eDeelzBUFNIpDCjdCsL55yIPqUsr6lmjpBPL0vea33QTMbcsSxu0umGXDbU
66juU4Z1jOE0wClIvaO699J+E2gBe1jUN6At6b8BSoZqCqXYoDHGei9RBUdvgqto
kVsoJfDI/TFMekYgpL5UVYmLdfgqLPPRP9pQBLDx3mszeAqnvfTICAzfXg==
-----END CERTIFICATE-----

View file

@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDeC7rZneOT2JwR
pa+PGG7WAigfI/oQCNzzMV12T2cdicHz2e/HioWfWeWy9PlVSosgl4C4jF5pV8hR
rkARvlpZklOn6Cl55w1pk0lbVa84AFsIKCrqBJQhtAGx6KxACvrkuWlA5uDTWYn5
DvMeYWxExb/VKETaSLvjN2J4N1HlZiSqzA9D+uPM5rqCmj8425EjJGw8xjpv2FS3
LW3sjm1V3FbFTrPLj3hYE7f7U+CqNQd3gW6lAG3FaKeh9SlGvmb5rEF3z1iWrcpO
DZ8KzbdZgKbFISJgecvPdAHuhFkg3onk3RbYdTZT0fo/R5Aj6FT3/v4DX6Xx7xZY
tegm3VkzAgMBAAECggEAfbVoWHGh+P7mBLuns7yiaziXXGHy3YcXB/vHrQZxG3D1
REf7Dx4mXjv35iP40Me+EnqTXmfhv4P6Hfwnu6Pn5NQJ1oKCkMg1eLLxEetloq/Y
2bpb6VckQhx15TIT6sIMntc1do//csg7a6yCCY4gTKMj4trEeEw7Svz9G5A+Jj9q
uAJWbXWpCw97Qu8qrIqmdobb1fbp5/9p54XmcgHAtYfYPcdNGlc4IeDZhMV5Pya7
7ULfeIIfAMWKG2iGKQfzGTqk2+2hZnc8y4SQc84hbL3GRphEr5cIz91NTcStRTC1
O+5Vzpw7A42ktDW1ZQwLQ6JHDubjXSFyy1bzz/Wn2QKBgQDpgi+yq0u84BJXYOVH
zzvKzMQv8aW9ezn6y7te3fbygU3x7F2GvMcpbju2xjSP8GBJTjpyEjqIjzP+fPgo
xERVbwrjzmJ1OrOiAkXQ9T3Y7TNzABG8XspEiXy+TpFFaeKy/rU5MTLc8RPGaOlW
bi8ePTjhJ7XvAIdrCcd/uibEnwKBgQDzbuXTqoh7i4wHGbbN8oThMJrMOsT4oiv0
Z5mtW5PMHSTu9GRt+sco3iYdFb/YCeqGUKQ7Lb5NWY5bmVc2OFh3TdwFC3zROebp
jT4Su9rVJpXj/GDXOZUH3u6DoERcvD+VtMcHummard6ROBX5cNOsCmugCL034LV9
1XMmgWNu7QKBgQDKSTOU7nvwJZT+CTzXFpnPt9AFUKuqGEREFZY+Or+hmY6yk3b6
MDPAmnQ0hEQopa5kEtbi1xPKFXSPdCdu+YfREx819iapM69GG/3rZWisseAuMdMr
glprQUfFfT6wCWiQc8L+xrYvXNEqwtvROiarZZIOy136rFSjz5b5+YN4NQKBgQDK
axmCYxgwGv5p3RDruVCS69acIfYthLQl+4uG4lJIdKeEZwWnidLXgbmRj8dBPiWc
YCvf5Y6LRP+h3STuufWd5skgDMhSNeJzq/XEoB48BWS3+eEQthndPJt6KecOcZ4x
vuuM2o37h749ZLSpAQ+Ry+xoWzvz3c8sfjPM/eQPlQKBgHGTKABY8IvQZ+dFY8Kj
VdXAfstMx9T/usfZHl/d5/J1brh18Sj+XGT/FMrG1Zk1++ql+IPWwGYaUtNntaQw
X4KGlLJUat2+aEYO3IcHrQqpZuq1Oy7peUNrOxCEtFTuWZQT2LLYa6JMOiMG5/Y7
WFGqCtnQc9LUg5a1h02k6Eyp
-----END PRIVATE KEY-----

View file

@ -0,0 +1,25 @@
-----BEGIN CERTIFICATE-----
MIIESzCCArOgAwIBAgIRAOw5c1g686jRWoMVoRgwfHgwDQYJKoZIhvcNAQELBQAw
gYMxHjAcBgNVBAoTFW1rY2VydCBkZXZlbG9wbWVudCBDQTEsMCoGA1UECwwjY2Fs
ZWJAY2FsZWItcGMtbGludXggKENhbGViIERveHNleSkxMzAxBgNVBAMMKm1rY2Vy
dCBjYWxlYkBjYWxlYi1wYy1saW51eCAoQ2FsZWIgRG94c2V5KTAeFw0yMTA4MTEy
MTU1NDZaFw0yMzExMTEyMjU1NDZaMFcxJzAlBgNVBAoTHm1rY2VydCBkZXZlbG9w
bWVudCBjZXJ0aWZpY2F0ZTEsMCoGA1UECwwjY2FsZWJAY2FsZWItcGMtbGludXgg
KENhbGViIERveHNleSkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDe
C7rZneOT2JwRpa+PGG7WAigfI/oQCNzzMV12T2cdicHz2e/HioWfWeWy9PlVSosg
l4C4jF5pV8hRrkARvlpZklOn6Cl55w1pk0lbVa84AFsIKCrqBJQhtAGx6KxACvrk
uWlA5uDTWYn5DvMeYWxExb/VKETaSLvjN2J4N1HlZiSqzA9D+uPM5rqCmj8425Ej
JGw8xjpv2FS3LW3sjm1V3FbFTrPLj3hYE7f7U+CqNQd3gW6lAG3FaKeh9SlGvmb5
rEF3z1iWrcpODZ8KzbdZgKbFISJgecvPdAHuhFkg3onk3RbYdTZT0fo/R5Aj6FT3
/v4DX6Xx7xZYtegm3VkzAgMBAAGjZTBjMA4GA1UdDwEB/wQEAwIFoDAdBgNVHSUE
FjAUBggrBgEFBQcDAgYIKwYBBQUHAwEwHwYDVR0jBBgwFoAUhYZYWIBHyk6ZVTnp
3lRt/tyBP00wEQYDVR0RBAowCIIGY2xpZW50MA0GCSqGSIb3DQEBCwUAA4IBgQAi
rSAwrwxiYIsAet5GWL+twMP1LiiifNY5REWJ1k/yu6hvG2bvAjSSVwO9pasMnD04
kwf5SvPcbWB/5hfwgYLJfDiuoYRptobLVXbvBtgj9EXzveJV6pZr9/oNaPBNArfr
a7N+R1IUNwnwJ5rIFdkpvrNjcxPbYV/XYb7LbuVuuQnmggUZDrKRKhoMIsq491tf
g6ZO5i4tBqDWUJk7S83AypTOBZNK2ugIQUWwq02gKEtchAACxg0jsuFD/wxtTQS2
TDY9pu0m4z01vNzpZy3oPkx48ZN2U9CGXaiEzjEhr87n8gkDBHmntCMJKbIiPmqD
YJvJSxQEHyGPuB5FcmC+yIWQ5FhoICynSUQlXn4gCH7EBEflf3TDoYBt0PNoW8Bc
xqfpLRltpIDWcOccmjtK+Ucpqgkvy3uAEo7GPfI8Dzv3m4FYG3GctO0RQgeA//VT
WsJPFK7HZdMiCJDEzZ0zvPg8O8Dcp17KW0KAn2BO3R7gZDrZ4lSNct3I91n69hI=
-----END CERTIFICATE-----

View file

@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDVDWPhOpNWAYNT
QZ4BR5ZU13HqRg/6B49duFcHPY+hkbQPSZdN+GZjCeRVIK8iAkgM3cvyRs40dygZ
eogu9LYo6AN/h6cVCF9ENg5jo7/PjK5/6aIf8/Ss22tCuhUL7UHV6ttf6y0+4Nq1
hRQcbyIPij89nmO+mT4Fhs9gNSsj2y0gQQWqN2lGhhBnnaCUxh3ElxIYQsCr85Fy
W8wWtPxn6mdFHc/iSUh0edeiExWsbPTdfEAj93J5bidXAi27uxTC8X2vHBBIbnZi
pb9zmZxBjDjslEnN4vVc9weW5N3nKcu+7QXJdiHFP32YSET2Opu3OIkJji4rpJqx
G1Z7MvPzAgMBAAECggEBAM3XhRO7+1QSXCaZdCZ6WuWXzojxrkf8++gpzXPCZ75L
vvMyP8xmXc38Za5VyL+MAr7joENxY5NPON/9AgyUBFdbat3RW323vAt0Ssy8Dfti
ScpuGWTT2CcWS/iJPwJp9bzPj6qJ1wo0Rzsv23FpcjgfcuB+4pHpDwJZ8IxcclTN
jv5XdmanN0Ai2ONDkIHQyvMTsYAX99OK7nXIs3OW7s4wsm8Wg+loCqTvojTzWuwE
TZNFonHAZ81jkrYfNjz+sM/tPuOYD+vWQ89+1IeQKFw1U0iBpF1VvhA7UeQZMeI8
S1NpDQTQW0kxmUAlLj7ldnIvknT/x0lKzoafVpk47/kCgYEA+SxnMLHe3Wxb4Kkf
7Gwktbth/wlWzUWzQ7c0TdhfEDjcRB7SeGIjrL4/HPyXEsCcGIj84TEob1EA0KVP
l6Jeqh5t/sr9da+uLFf6H41yZUaTccoyclnjHsqT+WLTtiTKqf7cXACg5NKbJwUT
ldCEu+4Ovur+8Ax6s/mGWNEzar0CgYEA2uOmD+SCIhj16P+3GnpZ0UzyDhUKedTy
LisZznroF6RI3BHzNT+YotHORDMiJtmX0slFcInAWaB3htLPbHmvredjlsH35eHW
B6wkWmbniJEovPysWdg7xjrj8DoL2dcm6liM1KpSo9k6XWJu36//xF4RTnL8JPEH
RPuBWmBXHG8CgYBjJy886lr0I61//eztKK+G/bTmRvIapzTJqnqOy54wl1/XX6iD
LRJjKCV3RHBdjvXOsZxnhCdB/KrlXBMLFRq0eX1t2Zr4nNsjXDL1IVU3Rdlge4SN
ioVdeGFf6Nq0bXmUIg3QMpPT2pbQ9S0w/ZQEMJv/jwW5wk2FlrLGXyElxQKBgQC3
skUzITp1Ey2NFM290uB93m1llBLum9+DD3jg6BTPgngC+K17Cpw2SI0qfx8yK3pW
08MK5xAeJ6Un6NNa3eSptX7GjpJUwmq0lasMkz/MRMZDlGmwHOBNRC729D/t2bo3
AYlvEGG6UBvDM1CJOVMUoT008Rrahczr/4ZXKnLw0QKBgExc+SXb5IRJIMHEQLkg
E7va23sR7x4j75mK6HnSwAM3jKx4GDgpkY1EO+rh+99mq/bIouL8ob/PG7A5RtKp
+Sgpqk5N6NpSFMaubsu1EQhqT5pmy0dN5KXecR4s1IylPvth/h3tdXPKGcLMD2M2
EN59YIA1o4qWjJsfEiuQ6x7M
-----END PRIVATE KEY-----

View file

@ -0,0 +1,26 @@
-----BEGIN CERTIFICATE-----
MIIEWDCCAsCgAwIBAgIRAK1MkqoHP+DPILewhMcnnu4wDQYJKoZIhvcNAQELBQAw
gYMxHjAcBgNVBAoTFW1rY2VydCBkZXZlbG9wbWVudCBDQTEsMCoGA1UECwwjY2Fs
ZWJAY2FsZWItcGMtbGludXggKENhbGViIERveHNleSkxMzAxBgNVBAMMKm1rY2Vy
dCBjYWxlYkBjYWxlYi1wYy1saW51eCAoQ2FsZWIgRG94c2V5KTAeFw0yMTA4MTEy
MTU0MzRaFw0yMzExMTEyMjU0MzRaMFcxJzAlBgNVBAoTHm1rY2VydCBkZXZlbG9w
bWVudCBjZXJ0aWZpY2F0ZTEsMCoGA1UECwwjY2FsZWJAY2FsZWItcGMtbGludXgg
KENhbGViIERveHNleSkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDV
DWPhOpNWAYNTQZ4BR5ZU13HqRg/6B49duFcHPY+hkbQPSZdN+GZjCeRVIK8iAkgM
3cvyRs40dygZeogu9LYo6AN/h6cVCF9ENg5jo7/PjK5/6aIf8/Ss22tCuhUL7UHV
6ttf6y0+4Nq1hRQcbyIPij89nmO+mT4Fhs9gNSsj2y0gQQWqN2lGhhBnnaCUxh3E
lxIYQsCr85FyW8wWtPxn6mdFHc/iSUh0edeiExWsbPTdfEAj93J5bidXAi27uxTC
8X2vHBBIbnZipb9zmZxBjDjslEnN4vVc9weW5N3nKcu+7QXJdiHFP32YSET2Opu3
OIkJji4rpJqxG1Z7MvPzAgMBAAGjcjBwMA4GA1UdDwEB/wQEAwIFoDATBgNVHSUE
DDAKBggrBgEFBQcDATAfBgNVHSMEGDAWgBSFhlhYgEfKTplVOeneVG3+3IE/TTAo
BgNVHREEITAfgh1pbnZhbGlkLmxvY2FsaG9zdC5wb21lcml1bS5pbzANBgkqhkiG
9w0BAQsFAAOCAYEABsSByXWA7e8hpKWZK4APWzkvDwiTGrDDE7k0hueJksTZ5Nqw
fRdGoUpweWIYzAv1etPAr+B2gsZM/jVRidaGDI1tKPytZ3pP6mQ52CVXkeJQytPr
rNDnP3Lbpbs8PHoHw3PVxIyRps1ZbZkgbUsXrSvpp/l+ZObbGQjr3Fdx5oXI6a1V
NNC39LkPhjTKtcG+H8dO5GRuDb/9PrzrnDwnl6CoORbEjTKRIFuA+vkFBRjyuccr
GQiMNmMxy5CMOsK+Od4+8qhv2ZgnREHyBnjFFhgVLFJ2PwUxk3N4GIzCC8tsD+vb
+YJgCS7n6JmcB9SFeyRy+qpolnfEaMvRwnJl6Evj17VCBy7x0gEO6B4lILPpziN8
VVhSuRsC0V8aXJJx89mwrg9pzN9w771rFVOCrAEdZei34/yfo8VyBbIR1gUxkRNJ
crTI9pT0PK+9OWQ57HtnGmFsPtWT8r7P8xukAPy50wSLF3InjEo8VR2df+V7DVVU
aTjNbuaG1NLNyWLH
-----END CERTIFICATE-----

View file

@ -0,0 +1,5 @@
-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIPRGWwLh75nNXnkk37zDfN8onLwfCiaLPTD+nc8Lx5hcoAoGCCqGSM49
AwEHoUQDQgAEkpBkO0TKmh4JdQfLOeeMd53Knga1WdQXr5Fcepk+dLVKdVKxXCGq
h1ojuhuW11GIoOzS9GoSKlNVSRFWVEWDvw==
-----END EC PRIVATE KEY-----

View file

@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC8HLBAIzXkPeeg
ldUfRKK2jQxSVZD5g+qsjAzpmrq/AtmweK1cGcOtZ6eOL+p8brPDyVhDT0QlI/O/
EKgCOFFxUDqoR82iY06SacAjHni6+PO9tVRbFV0w14BDAJSpB+VvWyl+FoPDV/vs
Z31FtYw+EwqkbDx/kaT9uzf+LJdlkf14nQQj8Eky/8d3mWJbb/9tjObsaQgJ5LLx
CYdImkr77X2LMuDw/1tpH642GE25Nrgm6QHlyKSfYXo38v83ebEqbZUDG+ZioArP
mqmkawUWw3ekhj80SJg/TK9PRaN/VvcI1PgAd7LZztUReSmTy5hd9r6rOBxpxwnT
DvHkBn6vAgMBAAECggEAB28i0AYUNSb1JnWFbKzruUctu3tCNXovJg6K3BiPVMkq
DT1XrJIgF5RHHOlr3OsLE6u7Xz2ctdML6PshiKTtIwtGpivgRpCiJEslmr2zi8AW
8eJeqRLZEfsSSJOXTG7RdGsn4qHFJ00s2ZTlcIHSPwnFm+XjJi99U8G4XsUoXo0r
Gy+0VCuU7M8gICEHHsrQO9XDD3nT2jiu5TjrKwjut3EmoJssI5bqx33+OBu5BpCP
CT473D43P9p3qi/XnfvqGSG2Oj4OajV4fr0o9B3KvIxkMem7WlI3jyy1kApyXqVT
bLkLFyWBNTWUZ2R/2wxmuoC6mLZw879MLCKMvk1doQKBgQDhmwGafJNymTiEQZRI
SsQx4seqfOKfgFC7ohqH9cROOu8IJ1o7q2pM2W4XiV+S3wTdPGmca6IOjX23isVB
2uqNi9S4MnI2/d22Gd/BR9rvBw1eGJoKbrWx22fE8QCEWT1AnO+DuD0jC85yRls7
axzlaMrxEu3LI9UE7NtrdQiByQKBgQDVdI6ceIVBT6RgvVGt8zkLjPIFjhQEHAIp
uhirgqpS6CX9Blyf2+o40zmfj3he5rCcEoB5MseM+DgFbcVh2e/MVnYiNNw6JCDB
BQkF408pZpSeKXvL/oyV/kImMTJ/tUDY0EXxMwSPJB0WltbWreVIHopigXRCbaey
uBHVBv/4twKBgHwHuePy5SU1s2qSmzD7Wc2LPfYu3nCOHNRrFGb26MuRfuReri7r
2G8TgoESFycp0QTIN8+1JM0XYKxNcJD6B8V1wKbbpQsymneI1gjutiB/Igw/PkDK
CL4VP4F4da5NWW1yWgNygLoJvZ/5qiKKisJc0GWk4HKz6mLgzOjQ2LJxAoGBALHZ
fN2YeYbyYcaM11p1VilulVTVjY3i/FZiDR4SL/IGJWjN/Szg4iXYsKFmu+dulOZl
cBALpEKrqpmzXYtrN6bsv18+5eO3qGbK2DrEq3eWVev2KoTMobxz7g++XBIWJmLA
Hhaa6IiPkYD5yyVyHKDbeXgb3o9eqCR7w7fYLjy/AoGAI4D+MFkivwUF7hqf5edS
KrltwmodHiqXNbVkwbW1AFPJbiYai4YFfK4IAbif/Ymxf9G78aOkr9ZpCIzOkDPZ
YpEwQGWsAhElCFvc8E/5dHESSp+tWtP+NluimpFqiDg3/SUnMwO2xH0nhLa0zejh
gmLh4w/CcPyb9ZyXceWU/nU=
-----END PRIVATE KEY-----

View file

@ -0,0 +1,26 @@
-----BEGIN CERTIFICATE-----
MIIEUjCCArqgAwIBAgIRAKNaEqCmmZfhmcYgZy01WCswDQYJKoZIhvcNAQELBQAw
gYMxHjAcBgNVBAoTFW1rY2VydCBkZXZlbG9wbWVudCBDQTEsMCoGA1UECwwjY2Fs
ZWJAY2FsZWItcGMtbGludXggKENhbGViIERveHNleSkxMzAxBgNVBAMMKm1rY2Vy
dCBjYWxlYkBjYWxlYi1wYy1saW51eCAoQ2FsZWIgRG94c2V5KTAeFw0yMTA4MTAx
NzMyMTBaFw0yMzExMTAxODMyMTBaMFcxJzAlBgNVBAoTHm1rY2VydCBkZXZlbG9w
bWVudCBjZXJ0aWZpY2F0ZTEsMCoGA1UECwwjY2FsZWJAY2FsZWItcGMtbGludXgg
KENhbGViIERveHNleSkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC8
HLBAIzXkPeegldUfRKK2jQxSVZD5g+qsjAzpmrq/AtmweK1cGcOtZ6eOL+p8brPD
yVhDT0QlI/O/EKgCOFFxUDqoR82iY06SacAjHni6+PO9tVRbFV0w14BDAJSpB+Vv
Wyl+FoPDV/vsZ31FtYw+EwqkbDx/kaT9uzf+LJdlkf14nQQj8Eky/8d3mWJbb/9t
jObsaQgJ5LLxCYdImkr77X2LMuDw/1tpH642GE25Nrgm6QHlyKSfYXo38v83ebEq
bZUDG+ZioArPmqmkawUWw3ekhj80SJg/TK9PRaN/VvcI1PgAd7LZztUReSmTy5hd
9r6rOBxpxwnTDvHkBn6vAgMBAAGjbDBqMA4GA1UdDwEB/wQEAwIFoDATBgNVHSUE
DDAKBggrBgEFBQcDATAfBgNVHSMEGDAWgBSFhlhYgEfKTplVOeneVG3+3IE/TTAi
BgNVHREEGzAZghcqLmxvY2FsaG9zdC5wb21lcml1bS5pbzANBgkqhkiG9w0BAQsF
AAOCAYEAufQAF79s7c1gmZ9CIKBSGkHh+SH01CuKYnnHiMowHsTioFaUAQsd/P4X
c2XBqc34eT3mCvpgZjHbjz6JlnTYJxuLvVqnVB3emtWrb1cQvh8BphxspTlS8uiE
AEf/ngtpzfA/f4lpGkzrQ0cyPkEJGz511q97itzn9RZZzVTZxNVFSP2vVhNNQVsW
OxakcvYRgnz8AOQS3OPHj2FQc3iibshct5leIwYZFcxINGHR6KL6+/LSePNCEMmK
qymVPkQGsIcU6GQ9fxaSu4mp+IUALProizEVI8SVk5nOm3HIez+ZfXhzfnGx06SI
6NuoQQPqUBeZeXn2YFYhipeRdrQxvA36/YXa/AkXCeU0pXxbtXKcvatfri5KnYJD
kH59a+aFkTsl41tfI2cnRYVddqXVl3OzLbcgAFLn1WeC1xx3xRXi7KldokOlvgv+
B6naWfCxRlWZ/lsmHae4kc1WH4Kc7nK+ITb40EkjV68/A7krZsN1VcqNtpomYkgE
xjUE8XUu
-----END CERTIFICATE-----

View file

@ -0,0 +1,40 @@
-----BEGIN PRIVATE KEY-----
MIIG/QIBADANBgkqhkiG9w0BAQEFAASCBucwggbjAgEAAoIBgQDWYpVeBSnee2cA
BYofSoWxGMyFaMQ0nJkY0UWM9ckyUh7VfgN+/aFSW2ZSmXuv5drcpi20z3elhPTe
98bANbj+/bi0015QWnMenK05ZK6qDtFwo/HVC/Ycaruu96+1J2toeWuEtykW3MCp
C1pHYS5g9iVDkpdrznvXKlYuSikjrj7K5toiTvum97LxKkuj6DXjapPD5vteSN1d
QgO9CS3sqlcwYA6RjUHwY2VEh2adP37BZrZwO+yJq9qF5y5Glgi8lN4cKlIlFUs/
xSpQsxNbNQXtN9mk4imYlZGzYYbbm+foBVPPboa5jVwKDpZ65mOs7JGP6yj+7V7U
BMFpW+gKmJtgh/kkAx185h93qwLFPc8/T7n++P1bu+fakXPGPE21rDeLPnUmucIZ
pJo5NpYVQv4WvTKq/zMR9Sspz2PFJnERTfTvq+F1q3ZNafEziPsB9oeSnjxwmaZO
SV0vXq/qeoqx4v6MBzVAY0/8R2LcpJ4ug0OZ3w0b2t6yo86P5Q8CAwEAAQKCAYEA
mE0Ey/xjGDkWnT9SNpSckYmKkiQxbybo5GaXQGLEAkmwuf6BwU+xsW5ZLMj4w6dJ
aoNr6Q2SdDYWN+hSe+4udIgPFfcrA26eZdrsfN937jwEsj7l6HJM7zcsCkrPuqQ8
e8X2ihwMxr8g53a3NgpmBmAXbP/RLrdL5zmea9gnjb+VwFNsF/+Aa8eAii9/+PtY
fS0TuJJ5dSvShxQEz+CbjYwd0LIM534wn/Qc1yhRz9hx1jd/2A/aJJu/7GylxWOD
mDwFOMcV0rU18r2e3u78GYTNozWCNE05mNhm7j8fYA4XVqR9wURFkBOPRSqg7Bd3
kyQmFQYonPW7xEHKOBGR2f3QUhB+ZKsM8ATpfXUmdoeVgx9Po+5FiqQVaNny1OIJ
vJeeYU++93ijZqvxvmgVmg97PxeA0CctTp2+ZFwmn1ZNTE7Zon/6lJ7/u999EyuX
s3uApNmjpBjA6davvgSIMdbn810f25yn2XVbzDrRJKSUM+Z6twUYZV53rxMWMQBR
AoHBAPRxozUFEUdz50dcuJYv2atfXR64T81XXD4i88K4DhEX60xCkTUFgVTgE0Y1
ZXgfe0ZNdGDm0zkQJsUcmxvtGCczYnbJZzOVXKfwyO8+z7dKXdXt0BQ7V7JvH50Y
KNh2QCtN1I7nZ5og7snDQkjOcrWD/JNNNkrW680O3lXeq4Cs8A6fCT73bX/KE53K
MltyxnfMpmGBqx+SwqLelvMY6fD44lhdIlOBi5/ACWCSMh4KnmIBVJTKKaNFdjKI
cv5bXQKBwQDghSlkKHuzdkzui8elNk5vfGl/IyUC3Ncx/ViF8kvZyErs4DgTKFT6
FmLx6mEboM4PQzofFLYtgmEkqVIo0FswrGS3BYgL5DGI3BSdWLO+Wqk07A3nRvhS
LvgM05Gwrin24Udu2ftrXbj6WVGix5en5xgLD0zopxDx5jISUADzH90V1ty6E5UX
W5JU1jVmRlWftg7kwfMJocm8PqgT49JSf9ryEV93Y6x5n+oxAAoorjUFmbS0j/Wo
VskI8FG/Z1sCgcBDQMyldrp1TTcxlBoZABtEIh2tqQoTtdhkJBuq1BbSryEGvz3S
N6yInInRBDnhnc+93OuLCZbNmVsBWkh2m3nMtz987Raew5ZVglLWOBLQG/7LL/3S
wyzyo84v24jJXWd0QpqboqEHb84i5rzi6SH4PNMN9+1bE9yWc2PKflPzOCFn9GuH
zm1q/j79Z7cJH//oz/5qz1E0g51XUCR5x739lYw4wY8DKJ4wmpY54p81UriWwET0
Ftbz29WUO6RfxOUCgcA/GO1C+qWZD9wbBil7YsG0TzCOzF+waEQKBnsEWc27TLDR
1UmtCJ6pEfWIqyfTTePbIjeJWJbCP2vxk6xFUBjwmuJLFUDgpqbNIZyhg7Yv/uai
utxFbQqIfi6z3BmLn8anXTRoENa5m8NKiCOLLbCPPUDiitBAagM3GExmHRnHOeM2
KgYqPSqfP9rmALVNIuMZWV7iJyeYQ2Ggh7NQs6v+B3SOpxc/REHKhIiacLpqKFs6
UbIZNIQDZTBmVLOEqRUCgcA8CZ4Q3OyltYcI0WkrJooNv1u9xdhW0Ch+wJ8JjXlw
6+BBsXQDci7UWH2sK6YFLCZFoL/K6BFHywi91R6vDGV9cPYL4LY4EEl0HHEuqGp3
o80O6s9fTwZYJYhxRBSRpI3f87LWM76CEVCwQl4D59eQXtvVLod5TL+71jMFddhb
L9a2u7f4Nz6iPNZyDrqiqzBzaBUpd79Xt1WGfdB0gyjm9YXAHh7UfS80xtGh5DTJ
dzutiHsOfQuQZN0aFKkMw18=
-----END PRIVATE KEY-----

View file

@ -0,0 +1,28 @@
-----BEGIN CERTIFICATE-----
MIIE2DCCA0CgAwIBAgIRALd9GaJR92qi7qL1eHGM6K0wDQYJKoZIhvcNAQELBQAw
gYMxHjAcBgNVBAoTFW1rY2VydCBkZXZlbG9wbWVudCBDQTEsMCoGA1UECwwjY2Fs
ZWJAY2FsZWItcGMtbGludXggKENhbGViIERveHNleSkxMzAxBgNVBAMMKm1rY2Vy
dCBjYWxlYkBjYWxlYi1wYy1saW51eCAoQ2FsZWIgRG94c2V5KTAeFw0yMTA4MTEy
MTU2MTBaFw0zMTA4MTEyMTU2MTBaMIGDMR4wHAYDVQQKExVta2NlcnQgZGV2ZWxv
cG1lbnQgQ0ExLDAqBgNVBAsMI2NhbGViQGNhbGViLXBjLWxpbnV4IChDYWxlYiBE
b3hzZXkpMTMwMQYDVQQDDCpta2NlcnQgY2FsZWJAY2FsZWItcGMtbGludXggKENh
bGViIERveHNleSkwggGiMA0GCSqGSIb3DQEBAQUAA4IBjwAwggGKAoIBgQDWYpVe
BSnee2cABYofSoWxGMyFaMQ0nJkY0UWM9ckyUh7VfgN+/aFSW2ZSmXuv5drcpi20
z3elhPTe98bANbj+/bi0015QWnMenK05ZK6qDtFwo/HVC/Ycaruu96+1J2toeWuE
tykW3MCpC1pHYS5g9iVDkpdrznvXKlYuSikjrj7K5toiTvum97LxKkuj6DXjapPD
5vteSN1dQgO9CS3sqlcwYA6RjUHwY2VEh2adP37BZrZwO+yJq9qF5y5Glgi8lN4c
KlIlFUs/xSpQsxNbNQXtN9mk4imYlZGzYYbbm+foBVPPboa5jVwKDpZ65mOs7JGP
6yj+7V7UBMFpW+gKmJtgh/kkAx185h93qwLFPc8/T7n++P1bu+fakXPGPE21rDeL
PnUmucIZpJo5NpYVQv4WvTKq/zMR9Sspz2PFJnERTfTvq+F1q3ZNafEziPsB9oeS
njxwmaZOSV0vXq/qeoqx4v6MBzVAY0/8R2LcpJ4ug0OZ3w0b2t6yo86P5Q8CAwEA
AaNFMEMwDgYDVR0PAQH/BAQDAgIEMBIGA1UdEwEB/wQIMAYBAf8CAQAwHQYDVR0O
BBYEFLcY8EoNofMcrrxzyxIn3W6ZOMVXMA0GCSqGSIb3DQEBCwUAA4IBgQCZzDCv
KIHX3GvjNSY5w5bOn4E3w7QHP09ABjT/wuT4LDkZHJMmlrLo3s8bcsQ0sMD1Y///
s07cp4xYlqD7BA0AcpvYVYq58xKxsoCwVXmG5cEeOoZmWf3qY2mS8eW96vOFrdIb
L4OF4xYUOMRqAOGAAr6VlO7gXa406HzrsA1hYZwreXhOTCZZPZOUnAu05SHFdgaM
TJNB/o01tpwQlrTxNmfropoOzyuvH0zU2RrMs0+EbOuC4A2cQ83DIFxvq67lyU0A
s1Q6tRM0+UDmJOLz3SdgN+D00hcuuj92GV4bH8BfyUv8NCY0vDij0TSjj4c4Qtc7
IPLTZ2g545oczhNgAmT7d+B5InyfiSIKemXqes2jpiAfzPNl9BVxsakcs/YzoYs1
+qTjAWuaDsKohEnO4BJuzv0xrce40enRgXyGGFvXu2s4FY2vJqTSo6ysDWnhI3LW
dcg6O2F4APCGGe7zsuqiqkpcknBabgzEs9foHq2mfo7XiEzedMN8BNqfSbA=
-----END CERTIFICATE-----

View file

@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCgVDM59lGzCRjd
UQCyzokqC4sEr7Ln2FpXfHjMWPuNK5vQYccTxto3JlAVXl+oOLHGoryKGDOkRV1S
Db3zAxYQNDuYUnraiVgLPrM9NFpHSk/IlACQjRlFRYG0Go3PDR2vJX4qTwgrqQtL
OJ5tHnqrt6idtvNp1ISYOIscXf/WIAhh+IuOvas4eie7GETX4eqPqpc6AEFuklmo
BHBfMCrGg89WBUTWCZYrHE9BYDL0LG/VwLYn2tDKBrS9iZIlTqPwve8VoGIlx4uv
HTdzaVStcRKOXCsbSwXRdt0842d4C1nohQkRHqHoBjQzrJiWJzxGmgByfa2rxbNg
15PFwF+ZAgMBAAECggEADTzGefunZTPUFLnSZ/D7jDglwz5KdC/9zYleY+jY5B/8
nmjkSfK6I6GLLSh8l2QO8YqQLIqxANglS1gNHdpcYPwfC4WL1S1P0qXboKsI5Sfy
jGoD3et4caq6ecdTfAvmLobW8uFRmGE9qHlFQ1cn47OnPVZUpKFCTVslyTLNo70h
28gx/lnpgkbeWotJ5GygE/H0jKJlG8/V3+Ppfuq6wypA5ELcGUeMAwmCfUNNlDy3
BhXSa6STgL26ar70KZIjTp9B97hIfDWObxgjzMX2JoiWXziszvbfaknfBsmfTm45
oUZYO0DuvLdLpxic0GZQwZCT6GzuexxJ9zR/pdahrQKBgQDEiwc0e+M1KaOoIIcw
V7pxoGjvd+CC5whS00jSf/rXPSPFxat9Ml5serOzLdRLM/NQ5wB9S7TYc6PJi3Mb
8pmbGadIXiGIJY8vX79P/velHT4csgULJAKJF9U65knhaidPPPmXloHOhRWrE8Zq
mexVgJZrHLI8197qmi+ctT5rEwKBgQDQ1J84AwI1hEsXHxoSetSznt+ae7pSUb/J
byqK9KEp0DLyf8GcS7vxyYGQo0mJDlHaJt56LKv+zdX4wGG85ztbOFVPee6XLKSs
I+h7rzc2hKrl+SaI91h1234WsTeJvfUSHyBy9vAwLhd0hplNrt7Tql5Z0VTWHmFE
2XbEwcTUIwKBgQDBpioHMDmBW/F/6ezJWOa+pco+h+KRl4i/8qVBog9Im1jvt/9r
b4FRaOQ9mt4c6qbGA5Sb30fkLKwoHFniI3ntM616xCRNvJQDnVcmPpVJ/jIAm/YU
L/q/kNfrHJOWobzxeaaCESz8imv7D5Tj25zb8cJC7xc+k4Nzq09WG83QOQKBgG28
LOZ7/j8tA2BlAYhQb1Dr3UgKWEBFoOgyuEJIhh+4vezb4VtGGL7XSnQ8ubmBgtWF
s0a0DrVYaGXMgg+H2pL2qS2YPx3FYcrrG5FS40qMsFkkcXFruFpGOp2mBi8lWJBr
NtvykwheUAj1ab1+dKz5S5ca/t99G1PYiiaeQ9XNAoGAVXk4HvdUc5q+BNiYvKUS
M2/TDU3cYY72mPCEw7G6Kpn6zMaakQcA1+Z8LkYcLaQKRD/66n99WWT+BcY+QXtC
0ZPHjeepDL8q+yXRY8zlcgAukg18Ta5yD1J1014y8UIV+HY8ongTni1sI8N+vKd4
+TF2C2Cynf5vQr5man7ShPw=
-----END PRIVATE KEY-----

View file

@ -0,0 +1,26 @@
-----BEGIN CERTIFICATE-----
MIIEUjCCArqgAwIBAgIRAKKYU7PSAFxZbhuLUlbv3iAwDQYJKoZIhvcNAQELBQAw
gYMxHjAcBgNVBAoTFW1rY2VydCBkZXZlbG9wbWVudCBDQTEsMCoGA1UECwwjY2Fs
ZWJAY2FsZWItcGMtbGludXggKENhbGViIERveHNleSkxMzAxBgNVBAMMKm1rY2Vy
dCBjYWxlYkBjYWxlYi1wYy1saW51eCAoQ2FsZWIgRG94c2V5KTAeFw0yMTA4MTEy
MTU2MTFaFw0yMzExMTEyMjU2MTFaMFcxJzAlBgNVBAoTHm1rY2VydCBkZXZlbG9w
bWVudCBjZXJ0aWZpY2F0ZTEsMCoGA1UECwwjY2FsZWJAY2FsZWItcGMtbGludXgg
KENhbGViIERveHNleSkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCg
VDM59lGzCRjdUQCyzokqC4sEr7Ln2FpXfHjMWPuNK5vQYccTxto3JlAVXl+oOLHG
oryKGDOkRV1SDb3zAxYQNDuYUnraiVgLPrM9NFpHSk/IlACQjRlFRYG0Go3PDR2v
JX4qTwgrqQtLOJ5tHnqrt6idtvNp1ISYOIscXf/WIAhh+IuOvas4eie7GETX4eqP
qpc6AEFuklmoBHBfMCrGg89WBUTWCZYrHE9BYDL0LG/VwLYn2tDKBrS9iZIlTqPw
ve8VoGIlx4uvHTdzaVStcRKOXCsbSwXRdt0842d4C1nohQkRHqHoBjQzrJiWJzxG
mgByfa2rxbNg15PFwF+ZAgMBAAGjbDBqMA4GA1UdDwEB/wQEAwIFoDATBgNVHSUE
DDAKBggrBgEFBQcDATAfBgNVHSMEGDAWgBS3GPBKDaHzHK68c8sSJ91umTjFVzAi
BgNVHREEGzAZghcqLmxvY2FsaG9zdC5wb21lcml1bS5pbzANBgkqhkiG9w0BAQsF
AAOCAYEAizMhh+VYIMp07wGn7+rzAE/651yiMC6kZHIOMHilvimyYvCf+Yc0MrcD
mVQgqlUpkn/f2SOFsBQonjAACkWlSHah9KStL0iTvOIH+oGLnv3Y9wrKvwJol3KR
c/+mO9R9TS71DoX+rTGRY3BNldpMBZF7HsYt/bg0RSpF0zkZarW+PEMmPw6IgIaD
RPGpOiQOqIxQn4d6MyiNGS0QmDeGSZvsC07ZcZ+JxsYi4S+yN6GXt11pstiRXjDv
zrO3s8TnVsBux7VDdIYfzMxqz+874MbsUUlb4txr3V48UDRLm7VDQ2/F+o0+Y5wt
XAnXTn/6GFpjJvPGr0A1QLOvnhR0DZ4Fl97athu44pqeQywDU5LPP3HqrWRXLy3j
BPBC4waHayL9Hnh4zQUe/h6hwC5Nxl/gqfB3Aaqr5PWX6rMFss8AYpB81ci+UJdm
KSIn/pMoK6TWkCveoQRQOZD8wfwPF4cUUmWcLFwSveZSiniFrAXQqZbO1k6RDhQf
havcwKlK
-----END CERTIFICATE-----

View file

@ -0,0 +1,119 @@
local MergeArrays(merge, arrays) =
std.foldl(function(acc, el) acc + el, arrays, []);
local MergeObjects(merge, objects) =
std.foldl(function(acc, el) {
[f]: if std.objectHas(acc, f) && std.objectHas(el, f) then
merge([acc[f], el[f]])
else if std.objectHas(el, f) then
el[f]
else
acc[f]
for f in std.setUnion(
std.set(std.objectFields(acc)),
std.set(std.objectFields(el))
)
}, objects, {});
local Merge(items) =
if std.foldl(function(acc, el) acc && std.isArray(el), items, true) then
MergeArrays(Merge, items)
else if std.foldl(function(acc, el) acc && std.isObject(el), items, true) then
MergeObjects(Merge, items)
else
items[std.length(items) - 1];
local KubernetesDeployment(name, image, command, ports) =
{
apiVersion: 'apps/v1',
kind: 'Deployment',
metadata: {
namespace: 'default',
name: name,
},
spec: {
replicas: 1,
selector: { matchLabels: { app: name } },
template: {
metadata: {
labels: { app: name },
},
spec: {
containers: [
{
name: name,
image: image,
ports: ports,
} + if std.type(command) == 'null' then {} else {
args: command,
},
],
},
},
},
};
local KubernetesService(name, ports) =
{
apiVersion: 'v1',
kind: 'Service',
metadata: {
namespace: 'default',
name: name,
labels: { app: name },
},
spec: {
selector: { app: name },
ports: ports,
},
};
local ParseURL(rawURL) =
{
local splitLeft(str, pat) =
local idxs = std.findSubstr(pat, str);
if std.length(idxs) == 0 then
[str, '']
else
[std.substr(str, 0, idxs[0]), std.substr(str, idxs[0] + std.length(pat), std.length(str))],
local splitRight(str, pat) =
local idxs = std.findSubstr(pat, str);
if std.length(idxs) == 0 then
['', str]
else
[std.substr(str, 0, idxs[0]), std.substr(str, idxs[0] + std.length(pat), std.length(str))],
local p0 = ['', rawURL],
local p1 = splitRight(p0[1], '://'),
local p2 = splitRight(p1[1], '@'),
local p3 = splitLeft(p2[1], '/'),
local p4 = splitLeft(p3[1], '?'),
local p5 = splitLeft(p4[1], '#'),
scheme: p1[0],
user: p2[0],
host: p3[0],
path: if p4[0] == '' then '' else '/' + p4[0],
query: p5[0],
fragment: p5[1],
};
local ComposeService(name, definition, additionalAliases=[]) =
{
[name]: definition {
networks: {
main: {
aliases: [name] + additionalAliases,
},
},
},
};
{
ComposeService: ComposeService,
Merge: Merge,
KubernetesDeployment: KubernetesDeployment,
KubernetesService: KubernetesService,
ParseURL: ParseURL,
}