Initial cdn::cache manifest

This commit is contained in:
Patrik Lundin 2024-10-10 15:22:11 +02:00
parent b2de8d246b
commit d0a19691aa
Signed by: patlu
GPG key ID: A0A812BA2249F294
5 changed files with 1071 additions and 0 deletions

View file

@ -0,0 +1,84 @@
# Configure a SUNET CDN CA server
class cdn::ca(
Hash[String, Integer] customers = {
customer1 => 1000000000,
}
)
{
include sunet::packages::certbot
include cdn::ca_trust
$cache_secrets = lookup({ 'name' => 'cdn::cache-secrets', 'default_value' => undef })
file { '/opt/sunet-cdn':
ensure => directory,
owner => 'root',
group => 'root',
mode => '0755',
}
file { '/opt/sunet-cdn/customers':
ensure => directory,
owner => 'root',
group => 'root',
mode => '0755',
}
file { '/opt/sunet-cdn/conf':
ensure => directory,
owner => 'root',
group => 'root',
mode => '0755',
}
file { "/opt/sunet-cdn/conf/varnish-slash-seccomp.json":
ensure => file,
owner => 'root',
group => 'root',
mode => '0644',
content => template('cdn/cache/varnish-slash-seccomp.json.erb'),
}
if $cache_secrets {
customers.each |String $customer, Integer $customer_uid| {
if $cache_secrets['customers'][$customer] {
file { "/opt/sunet-cdn/customers/$customer":
ensure => directory,
owner => $customer_uid,
group => $customer_uid,
mode => '0750',
}
file { "/opt/sunet-cdn/customers/$customer/shared":
ensure => directory,
owner => $customer_uid,
group => $customer_uid,
mode => '0750',
}
file { "/opt/sunet-cdn/customers/$customer/cache":
ensure => directory,
owner => $customer_uid,
group => $customer_uid,
mode => '0750',
}
file { "/opt/sunet-cdn/customers/$customer/conf/haproxy.cfg":
ensure => file,
owner => $customer_uid,
group => $customer_uid,
mode => '0440',
content => template('cdn/cache/haproxy.cfg.erb'),
}
sunet::docker_compose { "sunet-cdn-cache-$customer":
content => template('cdn/cache/docker-compose.yml.erb'),
service_name => "cdn-cache-$customer",
compose_dir => "/opt/sunet-cdn/compose/$customer",
compose_filename => 'docker-compose.yml',
description => "SUNET CDN CA $customer",
}
}
}
}
}

View file

@ -0,0 +1,79 @@
version: "3.9"
services:
haproxy:
# This is the official haproxy-supported container.
image: "docker.io/haproxytech/haproxy-debian:2.9.6"
volumes:
- /opt/sunet-cdn/customers/<%= @customer %>/conf/haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg:ro
- /opt/sunet-cdn/customers/<%= @customer %>/shared:/shared
- /opt/sunet-cdn/customers/<%= @customer %>/certs:/certs:ro
- /opt/sunet-cdn/customers/<%= @customer %>/certs-private:/certs-private:ro
# The general (outdated?) advice is never to start haproxy as an
# unprivileged user for production purposes:
# https://docs.haproxy.org/2.9/management.html
# ... but looking at https://hub.docker.com/_/haproxy it states:
# ===
# Note: the 2.4+ versions of the container will run as USER haproxy by
# default (hence the --sysctl net.ipv4.ip_unprivileged_port_start=0 above),
# but older versions still default to root for compatibility reasons; use
# --user haproxy (or any other UID) if you want to run as non-root in older
# versions.
# ===
# ... So it seems reasonable to use a custom user even for haproxy. The
# very large uid/gid is used to have a reasonable guarantee that nothing in
# the normal system will ever collide with it, and is inspired by what
# the OpenShift kubernetes system does:
# https://www.redhat.com/en/blog/a-guide-to-openshift-and-uids#
user: <%= @customer_uid %>:<%= @customer_uid %>
# Setting network_mode is done to not involve overhead of NAT, the
# idea is that it is one less state table to take care of in case of
# handling DDoS traffic, and it also means we will see real client IP
# addresses in haproxy for setting headers etc.
network_mode: "host"
# The following can not be used with network_mode: host so needs to be set
# in the global OS sysctl instead:
# ===
#sysctls:
# - net.ipv4.ip_unprivileged_port_start=80
# ===
varnish:
# We build our own varnish with the slash vmod present. We use the slash
# "fellow" storage backend to be able to persist cached content to disk, so
# it is retained in case of a restart of the container or machine.
image: "platform.sunet.se/sunet-cdn/cdn-varnish:af7f7d11e61acf9f6113811615d1baa46daf3bd1"
# Use the same custom user as is used for haproxy.
user: <%= @customer_uid %>:<%= @customer_uid %>
volumes:
- /opt/sunet-cdn/customers/<%= @customer %>/conf/default.vcl:/etc/varnish/default.vcl:ro
- /opt/sunet-cdn/customers/<%= @customer %>/shared:/shared
- /opt/sunet-cdn/customers/<%= @customer %>/cache:/cache
# From https://www.varnish-software.com/developers/tutorials/running-varnish-docker/:
# ===
# The /var/lib/varnish folder is frequently accessed by the varnishd
# program. Loading this folder into memory and accessing it through tmpfs
# would accelerate access to this folder.
# ===
# Using the subfolder "varnishd" is an updated expectation, see
# https://github.com/varnish/docker-varnish/issues/51
tmpfs:
- /var/lib/varnish/varnishd:exec,uid=<%= @customer_uid %>
# Varnish does not need to listen to any network ports as all communication
# between it and haproxy happens over a UNIX socket.
command: [
"-E",
"/usr/lib/varnish/vmods/libvmod_slash.so",
"-a",
"/shared/varnish.sock,PROXY,mode=600",
"-f",
"/etc/varnish/default.vcl",
"-p",
"feature=+http2",
"-s",
"fellow=fellow,/cache/fellow-storage,512MB,512MB,10MB",
]
# Allow io_uring operations expected by slash fellow.
security_opt:
- seccomp:/opt/sunet-cdn/conf/varnish-slash-seccomp.json
# Fix error log: "varnish-1 | Info: Child (29) said fellow_io_uring_register_buffers: fellow_io_uring_register_buffers Cannot allocate memory (12)"
ulimits:
memlock: -1

View file

@ -0,0 +1,61 @@
global
log stdout format raw local0 debug
stats socket /shared/admin.sock mode 660 level admin expose-fd listeners
stats timeout 30s
# Default SSL material locations
ca-base /etc/ssl/certs
crt-base /etc/ssl/private
# See: https://ssl-config.mozilla.org/#server=haproxy&server-version=2.0.3&config=intermediate
ssl-default-bind-ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384
ssl-default-bind-ciphersuites TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256
ssl-default-bind-options ssl-min-ver TLSv1.2 no-tls-tickets
defaults
log global
mode http
option httplog
timeout connect 5000
timeout client 50000
timeout server 50000
errorfile 400 /usr/local/etc/haproxy/errors/400.http
errorfile 403 /usr/local/etc/haproxy/errors/403.http
errorfile 408 /usr/local/etc/haproxy/errors/408.http
errorfile 500 /usr/local/etc/haproxy/errors/500.http
errorfile 502 /usr/local/etc/haproxy/errors/502.http
errorfile 503 /usr/local/etc/haproxy/errors/503.http
errorfile 504 /usr/local/etc/haproxy/errors/504.http
frontend customer
bind <%= @customer_ip %>:80
bind <%= @customer_ip %>:443 ssl crt /certs-private/customer.pem
default_backend varnish
backend varnish
server varnish /shared/varnish.sock send-proxy-v2 proxy-v2-options ssl
# https://varnish-cache.org/docs/trunk/users-guide/vcl-backends.html#connecting-through-a-proxy
listen haproxy_https
mode tcp
option tcplog
maxconn 1000
bind /shared/haproxy_https accept-proxy mode 640
stick-table type ip size 100
stick on dst
<% @cache_secrets['customers'][@customer]['origins'].each do |index, origin_ip| -%>
server s<%= index %> <%= origin_ip %>:443 ssl ca-file /etc/ssl/certs/ca-certificates.crt alpn http/1.1 sni fc_pp_authority
<% end -%>
# While varnish could recach out to HTTP backends itself, lets stick to one
# place (haproxy) for dealing with loadbalancing etc.
listen haproxy_http
mode tcp
option tcplog
maxconn 1000
bind /shared/haproxy_http accept-proxy mode 640
stick-table type ip size 100
stick on dst
<% @cache_secrets['customers'][@customer]['origins'].each do |index, origin_ip| -%>
server s<%= index %> <%= origin_ip %>:80
<% end -%>

View file

@ -0,0 +1,841 @@
{
"defaultAction": "SCMP_ACT_ERRNO",
"defaultErrnoRet": 1,
"archMap": [
{
"architecture": "SCMP_ARCH_X86_64",
"subArchitectures": [
"SCMP_ARCH_X86",
"SCMP_ARCH_X32"
]
},
{
"architecture": "SCMP_ARCH_AARCH64",
"subArchitectures": [
"SCMP_ARCH_ARM"
]
},
{
"architecture": "SCMP_ARCH_MIPS64",
"subArchitectures": [
"SCMP_ARCH_MIPS",
"SCMP_ARCH_MIPS64N32"
]
},
{
"architecture": "SCMP_ARCH_MIPS64N32",
"subArchitectures": [
"SCMP_ARCH_MIPS",
"SCMP_ARCH_MIPS64"
]
},
{
"architecture": "SCMP_ARCH_MIPSEL64",
"subArchitectures": [
"SCMP_ARCH_MIPSEL",
"SCMP_ARCH_MIPSEL64N32"
]
},
{
"architecture": "SCMP_ARCH_MIPSEL64N32",
"subArchitectures": [
"SCMP_ARCH_MIPSEL",
"SCMP_ARCH_MIPSEL64"
]
},
{
"architecture": "SCMP_ARCH_S390X",
"subArchitectures": [
"SCMP_ARCH_S390"
]
},
{
"architecture": "SCMP_ARCH_RISCV64",
"subArchitectures": null
}
],
"syscalls": [
{
"names": [
"accept",
"accept4",
"access",
"adjtimex",
"alarm",
"bind",
"brk",
"cachestat",
"capget",
"capset",
"chdir",
"chmod",
"chown",
"chown32",
"clock_adjtime",
"clock_adjtime64",
"clock_getres",
"clock_getres_time64",
"clock_gettime",
"clock_gettime64",
"clock_nanosleep",
"clock_nanosleep_time64",
"close",
"close_range",
"connect",
"copy_file_range",
"creat",
"dup",
"dup2",
"dup3",
"epoll_create",
"epoll_create1",
"epoll_ctl",
"epoll_ctl_old",
"epoll_pwait",
"epoll_pwait2",
"epoll_wait",
"epoll_wait_old",
"eventfd",
"eventfd2",
"execve",
"execveat",
"exit",
"exit_group",
"faccessat",
"faccessat2",
"fadvise64",
"fadvise64_64",
"fallocate",
"fanotify_mark",
"fchdir",
"fchmod",
"fchmodat",
"fchmodat2",
"fchown",
"fchown32",
"fchownat",
"fcntl",
"fcntl64",
"fdatasync",
"fgetxattr",
"flistxattr",
"flock",
"fork",
"fremovexattr",
"fsetxattr",
"fstat",
"fstat64",
"fstatat64",
"fstatfs",
"fstatfs64",
"fsync",
"ftruncate",
"ftruncate64",
"futex",
"futex_requeue",
"futex_time64",
"futex_wait",
"futex_waitv",
"futex_wake",
"futimesat",
"getcpu",
"getcwd",
"getdents",
"getdents64",
"getegid",
"getegid32",
"geteuid",
"geteuid32",
"getgid",
"getgid32",
"getgroups",
"getgroups32",
"getitimer",
"getpeername",
"getpgid",
"getpgrp",
"getpid",
"getppid",
"getpriority",
"getrandom",
"getresgid",
"getresgid32",
"getresuid",
"getresuid32",
"getrlimit",
"get_robust_list",
"getrusage",
"getsid",
"getsockname",
"getsockopt",
"get_thread_area",
"gettid",
"gettimeofday",
"getuid",
"getuid32",
"getxattr",
"inotify_add_watch",
"inotify_init",
"inotify_init1",
"inotify_rm_watch",
"io_cancel",
"ioctl",
"io_destroy",
"io_getevents",
"io_pgetevents",
"io_pgetevents_time64",
"ioprio_get",
"ioprio_set",
"io_setup",
"io_submit",
"ipc",
"kill",
"landlock_add_rule",
"landlock_create_ruleset",
"landlock_restrict_self",
"lchown",
"lchown32",
"lgetxattr",
"link",
"linkat",
"listen",
"listxattr",
"llistxattr",
"_llseek",
"lremovexattr",
"lseek",
"lsetxattr",
"lstat",
"lstat64",
"madvise",
"map_shadow_stack",
"membarrier",
"memfd_create",
"memfd_secret",
"mincore",
"mkdir",
"mkdirat",
"mknod",
"mknodat",
"mlock",
"mlock2",
"mlockall",
"mmap",
"mmap2",
"mprotect",
"mq_getsetattr",
"mq_notify",
"mq_open",
"mq_timedreceive",
"mq_timedreceive_time64",
"mq_timedsend",
"mq_timedsend_time64",
"mq_unlink",
"mremap",
"msgctl",
"msgget",
"msgrcv",
"msgsnd",
"msync",
"munlock",
"munlockall",
"munmap",
"name_to_handle_at",
"nanosleep",
"newfstatat",
"_newselect",
"open",
"openat",
"openat2",
"pause",
"pidfd_open",
"pidfd_send_signal",
"pipe",
"pipe2",
"pkey_alloc",
"pkey_free",
"pkey_mprotect",
"poll",
"ppoll",
"ppoll_time64",
"prctl",
"pread64",
"preadv",
"preadv2",
"prlimit64",
"process_mrelease",
"pselect6",
"pselect6_time64",
"pwrite64",
"pwritev",
"pwritev2",
"read",
"readahead",
"readlink",
"readlinkat",
"readv",
"recv",
"recvfrom",
"recvmmsg",
"recvmmsg_time64",
"recvmsg",
"remap_file_pages",
"removexattr",
"rename",
"renameat",
"renameat2",
"restart_syscall",
"rmdir",
"rseq",
"rt_sigaction",
"rt_sigpending",
"rt_sigprocmask",
"rt_sigqueueinfo",
"rt_sigreturn",
"rt_sigsuspend",
"rt_sigtimedwait",
"rt_sigtimedwait_time64",
"rt_tgsigqueueinfo",
"sched_getaffinity",
"sched_getattr",
"sched_getparam",
"sched_get_priority_max",
"sched_get_priority_min",
"sched_getscheduler",
"sched_rr_get_interval",
"sched_rr_get_interval_time64",
"sched_setaffinity",
"sched_setattr",
"sched_setparam",
"sched_setscheduler",
"sched_yield",
"seccomp",
"select",
"semctl",
"semget",
"semop",
"semtimedop",
"semtimedop_time64",
"send",
"sendfile",
"sendfile64",
"sendmmsg",
"sendmsg",
"sendto",
"setfsgid",
"setfsgid32",
"setfsuid",
"setfsuid32",
"setgid",
"setgid32",
"setgroups",
"setgroups32",
"setitimer",
"setpgid",
"setpriority",
"setregid",
"setregid32",
"setresgid",
"setresgid32",
"setresuid",
"setresuid32",
"setreuid",
"setreuid32",
"setrlimit",
"set_robust_list",
"setsid",
"setsockopt",
"set_thread_area",
"set_tid_address",
"setuid",
"setuid32",
"setxattr",
"shmat",
"shmctl",
"shmdt",
"shmget",
"shutdown",
"sigaltstack",
"signalfd",
"signalfd4",
"sigprocmask",
"sigreturn",
"socketcall",
"socketpair",
"splice",
"stat",
"stat64",
"statfs",
"statfs64",
"statx",
"symlink",
"symlinkat",
"sync",
"sync_file_range",
"syncfs",
"sysinfo",
"tee",
"tgkill",
"time",
"timer_create",
"timer_delete",
"timer_getoverrun",
"timer_gettime",
"timer_gettime64",
"timer_settime",
"timer_settime64",
"timerfd_create",
"timerfd_gettime",
"timerfd_gettime64",
"timerfd_settime",
"timerfd_settime64",
"times",
"tkill",
"truncate",
"truncate64",
"ugetrlimit",
"umask",
"uname",
"unlink",
"unlinkat",
"utime",
"utimensat",
"utimensat_time64",
"utimes",
"vfork",
"vmsplice",
"wait4",
"waitid",
"waitpid",
"write",
"writev"
],
"action": "SCMP_ACT_ALLOW"
},
{
"names": [
"process_vm_readv",
"process_vm_writev",
"ptrace"
],
"action": "SCMP_ACT_ALLOW",
"includes": {
"minKernel": "4.8"
}
},
{
"names": [
"socket"
],
"action": "SCMP_ACT_ALLOW",
"args": [
{
"index": 0,
"value": 40,
"op": "SCMP_CMP_NE"
}
]
},
{
"names": [
"personality"
],
"action": "SCMP_ACT_ALLOW",
"args": [
{
"index": 0,
"value": 0,
"op": "SCMP_CMP_EQ"
}
]
},
{
"names": [
"personality"
],
"action": "SCMP_ACT_ALLOW",
"args": [
{
"index": 0,
"value": 8,
"op": "SCMP_CMP_EQ"
}
]
},
{
"names": [
"personality"
],
"action": "SCMP_ACT_ALLOW",
"args": [
{
"index": 0,
"value": 131072,
"op": "SCMP_CMP_EQ"
}
]
},
{
"names": [
"personality"
],
"action": "SCMP_ACT_ALLOW",
"args": [
{
"index": 0,
"value": 131080,
"op": "SCMP_CMP_EQ"
}
]
},
{
"names": [
"personality"
],
"action": "SCMP_ACT_ALLOW",
"args": [
{
"index": 0,
"value": 4294967295,
"op": "SCMP_CMP_EQ"
}
]
},
{
"names": [
"sync_file_range2",
"swapcontext"
],
"action": "SCMP_ACT_ALLOW",
"includes": {
"arches": [
"ppc64le"
]
}
},
{
"names": [
"arm_fadvise64_64",
"arm_sync_file_range",
"sync_file_range2",
"breakpoint",
"cacheflush",
"set_tls"
],
"action": "SCMP_ACT_ALLOW",
"includes": {
"arches": [
"arm",
"arm64"
]
}
},
{
"names": [
"arch_prctl"
],
"action": "SCMP_ACT_ALLOW",
"includes": {
"arches": [
"amd64",
"x32"
]
}
},
{
"names": [
"modify_ldt"
],
"action": "SCMP_ACT_ALLOW",
"includes": {
"arches": [
"amd64",
"x32",
"x86"
]
}
},
{
"names": [
"s390_pci_mmio_read",
"s390_pci_mmio_write",
"s390_runtime_instr"
],
"action": "SCMP_ACT_ALLOW",
"includes": {
"arches": [
"s390",
"s390x"
]
}
},
{
"names": [
"riscv_flush_icache"
],
"action": "SCMP_ACT_ALLOW",
"includes": {
"arches": [
"riscv64"
]
}
},
{
"names": [
"open_by_handle_at"
],
"action": "SCMP_ACT_ALLOW",
"includes": {
"caps": [
"CAP_DAC_READ_SEARCH"
]
}
},
{
"names": [
"bpf",
"clone",
"clone3",
"fanotify_init",
"fsconfig",
"fsmount",
"fsopen",
"fspick",
"lookup_dcookie",
"mount",
"mount_setattr",
"move_mount",
"open_tree",
"perf_event_open",
"quotactl",
"quotactl_fd",
"setdomainname",
"sethostname",
"setns",
"syslog",
"umount",
"umount2",
"unshare"
],
"action": "SCMP_ACT_ALLOW",
"includes": {
"caps": [
"CAP_SYS_ADMIN"
]
}
},
{
"names": [
"clone"
],
"action": "SCMP_ACT_ALLOW",
"args": [
{
"index": 0,
"value": 2114060288,
"op": "SCMP_CMP_MASKED_EQ"
}
],
"excludes": {
"caps": [
"CAP_SYS_ADMIN"
],
"arches": [
"s390",
"s390x"
]
}
},
{
"names": [
"clone"
],
"action": "SCMP_ACT_ALLOW",
"args": [
{
"index": 1,
"value": 2114060288,
"op": "SCMP_CMP_MASKED_EQ"
}
],
"comment": "s390 parameter ordering for clone is different",
"includes": {
"arches": [
"s390",
"s390x"
]
},
"excludes": {
"caps": [
"CAP_SYS_ADMIN"
]
}
},
{
"names": [
"clone3"
],
"action": "SCMP_ACT_ERRNO",
"errnoRet": 38,
"excludes": {
"caps": [
"CAP_SYS_ADMIN"
]
}
},
{
"names": [
"reboot"
],
"action": "SCMP_ACT_ALLOW",
"includes": {
"caps": [
"CAP_SYS_BOOT"
]
}
},
{
"names": [
"chroot"
],
"action": "SCMP_ACT_ALLOW",
"includes": {
"caps": [
"CAP_SYS_CHROOT"
]
}
},
{
"names": [
"delete_module",
"init_module",
"finit_module"
],
"action": "SCMP_ACT_ALLOW",
"includes": {
"caps": [
"CAP_SYS_MODULE"
]
}
},
{
"names": [
"acct"
],
"action": "SCMP_ACT_ALLOW",
"includes": {
"caps": [
"CAP_SYS_PACCT"
]
}
},
{
"names": [
"kcmp",
"pidfd_getfd",
"process_madvise",
"process_vm_readv",
"process_vm_writev",
"ptrace"
],
"action": "SCMP_ACT_ALLOW",
"includes": {
"caps": [
"CAP_SYS_PTRACE"
]
}
},
{
"names": [
"iopl",
"ioperm"
],
"action": "SCMP_ACT_ALLOW",
"includes": {
"caps": [
"CAP_SYS_RAWIO"
]
}
},
{
"names": [
"settimeofday",
"stime",
"clock_settime",
"clock_settime64"
],
"action": "SCMP_ACT_ALLOW",
"includes": {
"caps": [
"CAP_SYS_TIME"
]
}
},
{
"names": [
"vhangup"
],
"action": "SCMP_ACT_ALLOW",
"includes": {
"caps": [
"CAP_SYS_TTY_CONFIG"
]
}
},
{
"names": [
"get_mempolicy",
"mbind",
"set_mempolicy",
"set_mempolicy_home_node"
],
"action": "SCMP_ACT_ALLOW",
"includes": {
"caps": [
"CAP_SYS_NICE"
]
}
},
{
"names": [
"syslog"
],
"action": "SCMP_ACT_ALLOW",
"includes": {
"caps": [
"CAP_SYSLOG"
]
}
},
{
"names": [
"bpf"
],
"action": "SCMP_ACT_ALLOW",
"includes": {
"caps": [
"CAP_BPF"
]
}
},
{
"names": [
"perf_event_open"
],
"action": "SCMP_ACT_ALLOW",
"includes": {
"caps": [
"CAP_PERFMON"
]
}
},
{
"names": [
"io_uring_enter",
"io_uring_register",
"io_uring_setup"
],
"action": "SCMP_ACT_ALLOW"
}
]
}

View file

@ -0,0 +1,6 @@
---
cdn::cache-secrets:
customers:
customer1:
origins:
- ENC[PKCS7,MIIC6wYJKoZIhvcNAQcDoIIC3DCCAtgCAQAxggKTMIICjwIBADB3MF8xCzAJBgNVBAYTAlNFMQ4wDAYDVQQKDAVTVU5FVDEOMAwGA1UECwwFRVlBTUwxMDAuBgNVBAMMJ2ludGVybmFsLXN0bzMtdGVzdC1jYWNoZS0xLmNkbi5zdW5ldC5zZQIUJEYAJSDlgRqHR7z1XpIgwd3vL7gwDQYJKoZIhvcNAQEBBQAEggIAlcofAryMHgzdzYKeslCB7ekukxm30xOyYVHusJ4mwylU82xZjtXpcneoPJKydc2bSSP6LkuGGSaiziY1XdU/TLid9fhfBtSFM1S8Cc8IoAgZd25wXo0eaf2f5AfDcSJkywW+kfySEmXCGPGaCeo25k8zZDhWvI4ZrNBDgCTksuL99HWjsT6T6orC9qu2GEBWSpXcKbKu/Yv/1XbNvsJpW1XgwkGJz2EXjJSLKljhBG4P7i8tHIGHbJn/Xo6IWgsBqi8elc5sb/gK4LE5k+JSv78M7ZRs7aRaBm25LZnvNKBPCpyy9sgDEbJzGsHOTtfuyPpgra34wVy/Vt2XFOFserbg/4ZBjkCEN1pNSjb4jDh1tedlvjV1Ovf4W5EQcE1eu2UWvyfLLjjPHz0nW2OQbJB1zO6XOEdP2ot4+WnSgsZvCv9kGJPRhbbrdLVwW/5eSttbaWPo0viNavvTSgjYBiJ38LhPmpulHzhsCSFZisjq/ue6oFhoUwHb/pMklGQRJ2i2AqzPECstCTuKpSMTMrlnAgFS7u3DDXjmeYlV0UlF1SlEpGYqkGQspfWI+7DAnDomVDGAL8M6/+xpaudTNbcTvbrbPh0kwUjKR4+cP5mUAbNEho5UxI5tGnIRWRRd560+FKrAKPY/0V/Dx/tVpu7FgrgOEScdExMzAfDrTSwwPAYJKoZIhvcNAQcBMB0GCWCGSAFlAwQBKgQQz45S0wVsGWF9YXUE7fQwd4AQPn+7I2vxK/66O56enFIfQQ==]