Skip to content

Commit 08854dc

Browse files
committed
add etcd.mandatory and max_connections_per_user options
1 parent 2da9e39 commit 08854dc

File tree

19 files changed

+339
-213
lines changed

19 files changed

+339
-213
lines changed

cmd/sshproxy/sshproxy.go

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,6 @@ func setEnvironment(environment map[string]string) {
167167
}
168168
}
169169

170-
//
171170
func usage() {
172171
fmt.Fprintf(os.Stderr, "usage: sshproxy [config]\n")
173172
flag.PrintDefaults()
@@ -318,6 +317,7 @@ func mainExitCode() int {
318317
for k, v := range config.Routes {
319318
log.Debugf("config.routes.%s = %+v", k, v)
320319
}
320+
log.Debugf("config.max_connections_per_user = %d", config.MaxConnectionsPerUser)
321321
log.Debugf("config.ssh.exe = %s", config.SSH.Exe)
322322
log.Debugf("config.ssh.args = %v", config.SSH.Args)
323323

@@ -329,6 +329,24 @@ func mainExitCode() int {
329329
log.Errorf("Cannot contact etcd cluster to update state: %v", err)
330330
}
331331

332+
if cli != nil && cli.IsAlive() {
333+
if config.MaxConnectionsPerUser > 0 {
334+
userConnectionsCount, err := cli.GetUserConnectionsCount(username)
335+
if err != nil {
336+
log.Fatalf("Getting user connections count: %s", err)
337+
}
338+
log.Debugf("Number of connections of %s: %d", username, userConnectionsCount)
339+
if userConnectionsCount >= config.MaxConnectionsPerUser {
340+
fmt.Fprintln(os.Stderr, "Too many simultaneous connections")
341+
log.Fatalf("Max connections per user reached for %s", username)
342+
}
343+
}
344+
} else {
345+
if config.Etcd.Mandatory {
346+
log.Fatal("Etcd is mandatory but unavailable")
347+
}
348+
}
349+
332350
service, hostport, forceCommand, commandMustMatch, etcdKeyTTL, environment, err := findDestination(cli, username, config.Routes, sshInfos.Dst(), config.CheckInterval)
333351
switch {
334352
case err != nil:

config/sshproxy.yaml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,8 @@
9191
# - password: password if basic authentication is enabled.
9292
# - keyttl: time to live in second for a connection stored in etcd after it has
9393
# ended. Default is 5 seconds.
94+
# - mandatory: if true, connections will be allowed only if etcd is available.
95+
# Default is false.
9496
#etcd:
9597
# endpoints:
9698
# - "host1:port1"
@@ -102,6 +104,7 @@
102104
# username: ""
103105
# password: ""
104106
# keyttl: 5
107+
# mandatory: false
105108

106109
# Environment.
107110
# Environment variables can be set if needed. The '{user}' pattern will be
@@ -114,6 +117,11 @@
114117
# exe: ssh
115118
# args: ["-q", "-Y"]
116119

120+
# Maximum number of connections allowed per user. Connections are counted in
121+
# the etcd database. If set to 0, there is no limit number of connections per
122+
# user. Default is 0.
123+
#max_connections_per_user: 0
124+
117125
# Routes definition.
118126
# The key is the service name (only used for display). The special service name
119127
# "default" can be used to define a default route and does not need the source

doc/sshproxy.yaml.txt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,11 @@ It can also be a network address where to send dumps if specified as
9999
suffix such as 'h', 'm' and 's' (e.g. '2m30s'). These statistics are
100100
only available when the 'dump' option is set.
101101

102+
*max_connections_per_user*::
103+
an integer setting the maximum number of connections allowed per user.
104+
Connections are counted in the etcd database. If set to 0, there is no
105+
limit number of connections per user. Default is 0.
106+
102107
Commands can be translated between what is received by sshproxy and what is
103108
executed by the ssh forked by sshproxy. *translate_commands* is an associative
104109
array whose keys are strings containing the exact user command. *ssh_args*
@@ -150,6 +155,10 @@ etcd configuration is provided in an associative array *etcd* whose keys are:
150155
is up. It will be removed from etcd after this number of seconds.
151156
Default is 5 seconds.
152157

158+
*mandatory*::
159+
a boolean. If true, connections will be allowed only if etcd is
160+
available. Default is false.
161+
153162
For example, we can have the following:
154163

155164
etcd:
@@ -161,6 +170,7 @@ For example, we can have the following:
161170
certfile: "/etc/sshproxy/sshproxy.pem"
162171
username: "sshproxy"
163172
password: "sshproxypassword"
173+
mandatory: true
164174

165175
An associative array *environment* can be used to set environment variables.
166176
The pattern '\{user}' will be replaced with the user login:

pkg/record/record.go

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -18,16 +18,17 @@
1818
// - data.
1919
//
2020
// A file record has a header with the following fields:
21-
// - an unsigned 16 bits integer for the version number,
22-
// - an unsigned 16 bits integer indicating the header size (from byte 0 to the
23-
// start of the first record),
24-
// - an unsigned 128 bits integer for the source IP address,
25-
// - an unsigned 16 bits integer for the source port,
26-
// - an unsigned 128 bits integer for the destination IP address,
27-
// - an unsigned 16 bits integer for the destination port,
28-
// - an unsigned 64 bits integer for the start of connection (in ns),
29-
// - a NULL terminated string with the user name,
30-
// - a NULL terminated string with the command run by the user (can be empty
21+
// - an unsigned 16 bits integer for the version number,
22+
// - an unsigned 16 bits integer indicating the header size (from byte 0 to the
23+
// start of the first record),
24+
// - an unsigned 128 bits integer for the source IP address,
25+
// - an unsigned 16 bits integer for the source port,
26+
// - an unsigned 128 bits integer for the destination IP address,
27+
// - an unsigned 16 bits integer for the destination port,
28+
// - an unsigned 64 bits integer for the start of connection (in ns),
29+
// - a NULL terminated string with the user name,
30+
// - a NULL terminated string with the command run by the user (can be empty
31+
//
3132
// with only its NULL end).
3233
//
3334
// All integers are big endian.

pkg/utils/config.go

Lines changed: 37 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -29,23 +29,24 @@ var (
2929

3030
// Config represents the configuration for sshproxy.
3131
type Config struct {
32-
Debug bool
33-
Log string
34-
CheckInterval Duration `yaml:"check_interval"`
35-
ErrorBanner string `yaml:"error_banner"`
36-
Dump string
37-
DumpLimitSize uint64 `yaml:"dump_limit_size"`
38-
DumpLimitWindow Duration `yaml:"dump_limit_window"`
39-
Etcd etcdConfig
40-
EtcdStatsInterval Duration `yaml:"etcd_stats_interval"`
41-
LogStatsInterval Duration `yaml:"log_stats_interval"`
42-
BgCommand string `yaml:"bg_command"`
43-
SSH sshConfig
44-
TranslateCommands map[string]*TranslateCommandConfig `yaml:"translate_commands"`
45-
Environment map[string]string
46-
Routes map[string]*RouteConfig
47-
Users []map[string]subConfig
48-
Groups []map[string]subConfig
32+
Debug bool
33+
Log string
34+
CheckInterval Duration `yaml:"check_interval"`
35+
ErrorBanner string `yaml:"error_banner"`
36+
Dump string
37+
DumpLimitSize uint64 `yaml:"dump_limit_size"`
38+
DumpLimitWindow Duration `yaml:"dump_limit_window"`
39+
Etcd etcdConfig
40+
EtcdStatsInterval Duration `yaml:"etcd_stats_interval"`
41+
LogStatsInterval Duration `yaml:"log_stats_interval"`
42+
BgCommand string `yaml:"bg_command"`
43+
SSH sshConfig
44+
TranslateCommands map[string]*TranslateCommandConfig `yaml:"translate_commands"`
45+
Environment map[string]string
46+
Routes map[string]*RouteConfig
47+
MaxConnectionsPerUser int `yaml:"max_connections_per_user"`
48+
Users []map[string]subConfig
49+
Groups []map[string]subConfig
4950
}
5051

5152
// TranslateCommandConfig represents the configuration of a translate_command.
@@ -82,6 +83,7 @@ type etcdConfig struct {
8283
Username string
8384
Password string
8485
KeyTTL int64
86+
Mandatory bool
8587
}
8688

8789
type etcdTLSConfig struct {
@@ -93,19 +95,20 @@ type etcdTLSConfig struct {
9395
// We use interface{} instead of real type to check if the option was specified
9496
// or not.
9597
type subConfig struct {
96-
Debug interface{}
97-
Log interface{}
98-
ErrorBanner interface{} `yaml:"error_banner"`
99-
Dump interface{}
100-
DumpLimitSize interface{} `yaml:"dump_limit_size"`
101-
DumpLimitWindow interface{} `yaml:"dump_limit_window"`
102-
EtcdStatsInterval interface{} `yaml:"etcd_stats_interval"`
103-
LogStatsInterval interface{} `yaml:"log_stats_interval"`
104-
BgCommand interface{} `yaml:"bg_command"`
105-
TranslateCommands map[string]*TranslateCommandConfig `yaml:"translate_commands"`
106-
Environment map[string]string
107-
Routes map[string]*RouteConfig
108-
SSH sshConfig
98+
Debug interface{}
99+
Log interface{}
100+
ErrorBanner interface{} `yaml:"error_banner"`
101+
Dump interface{}
102+
DumpLimitSize interface{} `yaml:"dump_limit_size"`
103+
DumpLimitWindow interface{} `yaml:"dump_limit_window"`
104+
EtcdStatsInterval interface{} `yaml:"etcd_stats_interval"`
105+
LogStatsInterval interface{} `yaml:"log_stats_interval"`
106+
BgCommand interface{} `yaml:"bg_command"`
107+
TranslateCommands map[string]*TranslateCommandConfig `yaml:"translate_commands"`
108+
Environment map[string]string
109+
Routes map[string]*RouteConfig
110+
MaxConnectionsPerUser interface{} `yaml:"max_connections_per_user"`
111+
SSH sshConfig
109112
}
110113

111114
func parseSubConfig(config *Config, subconfig *subConfig) error {
@@ -170,6 +173,10 @@ func parseSubConfig(config *Config, subconfig *subConfig) error {
170173
config.Routes[service] = opts
171174
}
172175

176+
if subconfig.MaxConnectionsPerUser != nil {
177+
config.MaxConnectionsPerUser = subconfig.MaxConnectionsPerUser.(int)
178+
}
179+
173180
// merge translate_commands
174181
for k, v := range subconfig.TranslateCommands {
175182
config.TranslateCommands[k] = v

pkg/utils/etcd.go

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,10 @@ import (
3232
type State int
3333

3434
// These are the possible states of an host:
35-
// Up: host is up,
36-
// Down: host is down,
37-
// Disabled: host was disabled by an admin.
35+
//
36+
// Up: host is up,
37+
// Down: host is down,
38+
// Disabled: host was disabled by an admin.
3839
const (
3940
Unknown State = iota
4041
Up
@@ -529,6 +530,35 @@ func (c *Client) GetAllConnections() ([]*FlatConnection, error) {
529530
return conns, nil
530531
}
531532

533+
// GetUserConnectionsCount returns the number of active connections of a user, based on etcd.
534+
func (c *Client) GetUserConnectionsCount(username string) (int, error) {
535+
ctx, cancel := context.WithTimeout(context.Background(), c.requestTimeout)
536+
resp, err := c.cli.Get(ctx, etcdConnectionsPath, clientv3.WithPrefix(), clientv3.WithSort(clientv3.SortByKey, clientv3.SortAscend))
537+
cancel()
538+
if err != nil {
539+
return 0, err
540+
}
541+
542+
count := 0
543+
for _, ev := range resp.Kvs {
544+
subkey := string(ev.Key)[len(etcdConnectionsPath)+1:]
545+
fields := strings.Split(subkey, "/")
546+
if len(fields) != 4 {
547+
return 0, fmt.Errorf("bad key format %s", subkey)
548+
}
549+
550+
subfields := strings.Split(fields[0], "@")
551+
if len(subfields) != 2 {
552+
return 0, fmt.Errorf("bad subkey format %s", fields[0])
553+
}
554+
if subfields[0] == username {
555+
count++
556+
}
557+
}
558+
559+
return count, nil
560+
}
561+
532562
// FlatHost is a structure used to flatten a host informations present in etcd.
533563
type FlatHost struct {
534564
Hostname string
@@ -539,7 +569,7 @@ type FlatHost struct {
539569
*Host
540570
}
541571

542-
// GetUserHosts returns a list of hosts used by a user, based on etcd.
572+
// GetUserHosts returns a list of hosts used by a user@service, based on etcd.
543573
func (c *Client) GetUserHosts(key string) (map[string]*FlatHost, error) {
544574
ctx, cancel := context.WithTimeout(context.Background(), c.requestTimeout)
545575
resp, err := c.cli.Get(ctx, etcdConnectionsPath, clientv3.WithPrefix(), clientv3.WithSort(clientv3.SortByKey, clientv3.SortAscend))

pkg/utils/route.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import (
2020
"github.com/op/go-logging"
2121
)
2222

23-
var mylog = logging.MustGetLogger("sshproxy/route")
23+
var mylog = logging.MustGetLogger("sshproxy")
2424

2525
type selectDestinationFunc func([]string, HostChecker, *Client, string) (string, error)
2626

test/centos-image/etcd/ca-key.pem

Lines changed: 25 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,27 @@
11
-----BEGIN RSA PRIVATE KEY-----
2-
MIIEpQIBAAKCAQEArhn0BdvFSr82bez8E7351GfniSt8Kq3VVbpk+83L5wTFgAZT
3-
2vrdU+a9+K3xiZeGkrs7Rm0QTlwHZ/BxnwotpW1m6wABhkdPNC2JSccZOrxuMCA5
4-
mw7EKTtfe3QuKzVy17090OHLuzzmJHETFAavldm1nhaBLImxb/uDni3spr20QW6p
5-
42lc7m3ZpUv9m54/FlBzP601sTfp2j43+s3qoU+zuTJ0aeLM96fyykEBA6nC9wLf
6-
YUlEny6jg/RHTquZ05tzyexUH0ZS1fGSEZHQhAhmRCW5IV5xsbHfWnQgYoyO9ART
7-
in74e01u7QRPxKOlWNbH6fTq5kHIbIxPKkyckwIDAQABAoIBAQCUN1IQXr3CWbDL
8-
upILeJMkqeRqWZPWfbD9z7ZCzDvHi79bs4dWCHW+BNU7zVGwskgFyNG4rgbQvvYr
9-
HX6NR8v+ZnnUnGev2itqn02eeppW763a2MRR5itHct1hq1HWxSTk1521o1SfLUQ8
10-
R+tXBC4dI3/DeyMzOHHPhM7TLgOnGrdnq6kaNSCEvHekkxujyd/T3DF1/EFzwkJi
11-
af/3zRn4/KYLiKg68Eu3Paxd58yflwL/eiobxAd/XL6Vra/8vSoJrbY2wTC1dRm/
12-
rcT9otkzvXnje3l9tBL1rOZvP5Pwj7czzmoMiHEnHz01gHitweA7yM0fMXM6V0dN
13-
8fCL8nyBAoGBAN1+GZYzer8c4NZm+97wL69VMUJwmdzyXiSUzdfoXY5T3eM50dAS
14-
0t5kUI+uTJvdRUcqKrVHd862LfY1eCsAdOahjjucRnE7y5eB7us+Jw2kAdbfLYmY
15-
zNwXda7Hr8oSEZD08ZAc6SSsFaXIBZHHNG3l0cCyT+iYY1+iPW9e2yhBAoGBAMk5
16-
ueq+4nVgiyAGs+zrEN5PDxkDPzIl3ay2CnX3/uZgPFwMSc5+11XzElx4w2SuH5KN
17-
P6Jw2/wyXrBQizLftMo5sUTER0MsR5kJGj8mOqE4nXlVaJjEcc+zqiQL2+ut+G9r
18-
QWjc2MvecxbRZWbljvo7iWPPow1rD/hcJPPRpq/TAoGAElWq5o0SXxmT3wudU+IM
19-
AHaL4lUR14FUm7CZrYK1o997YaDapl+4huJE7O/ftZNGddKewGzPFRRtj5DzhpO+
20-
GY1MCdEw35S0kLEXxu9ZKj7mTBQjX91/L8TdX/91pMOEIryz9lr+TaPVEukQ1PVp
21-
eKNqjon0JWvf5f23Gi3+9EECgYEAkSXDlrGUFkjKchKDrIyuwTzTzh4KcrjfmppB
22-
F3EIe9LFFzyB7KKP316TSwKNJs121rZuRVUVSWhn5tRiZBL7jLdqyAiwwqJpDtmD
23-
BjQkLHDzHjdeWHuzALg/LPsk6m61+mCqcOKEuyG9cKMgesVxicxyQP3gRmiIv4Rv
24-
3zPGAaMCgYEAtmQX1I8IgKlr8FrscwjyUSUYW85dLL77LBzYEwJMmmMP6XFMJoxt
25-
mR/UcG/On1PSPq0mZljtQiXoz7vhwBwPuvKgDyh1oJ6kaBILxKiYpECv1ncyR7an
26-
g+uLOVccnb5X7/kEDdgaDxhpYxj/QZITdL24fs5BJvOJpYuF3nLkaJk=
2+
MIIEowIBAAKCAQEA10QIT3hacop0YnAxwo/vWdIM+FVHDw7IzI5KoGtMIkW0Z7Yy
3+
adHru/fnNPFmgKWB4Uoc6RBdVoxj2RxevWELsDbWx6M+yz9NJBsQBvhfFHVF+F36
4+
Y/0FYqGd4kSDsXepeuQwfG3u68HfDmuwceQuRYndUICyV2TJd/d5boTCmQodvHiu
5+
gMg7xKhsp4D2Ty7MwI5134JJc6HgxEj17JXNOyYHSNi3+oi9YBbgJnkSvDK4CYWV
6+
yTJg9Q0cmzy16t7wq31/1xClTPFtPBKyD1lQ60QAfAyYVTskox98n8wJexaFWIcW
7+
VpSbIzra8lyEpPtI6dtJ26XtrLC7Ftd0lbAtvwIDAQABAoIBAQDEo3bb8Vrd6q/q
8+
BAbQbd78s635YxzoGl8+zHRLGfiQDhB/9KUUmRe3uvrPF/zEY2vDOooyNCqvFstW
9+
1l2MrQuaSl3kO7DuxMJYywfW9icUbxQdjutdTxIOSQgKWv/Zqy97/Np1r45iedSi
10+
PakOQJwsFYW/NfCzeHO5TKz2UfvZAwHIpoLUXGzor44ImKU50hMCJ0hVJ5rxlNrH
11+
PwFo3T91DITj0B9UFoi0pc7jeZcLmPkwk3HNhOb3qD/+BImuZszQQHxXAiXa2zky
12+
hgQ4GFPQMAN7acWWQSE6ePjlkff+XX8TGNi7oTYls0MT/DblL9rfPEh0Fr8Wx7yw
13+
0vw0xFSBAoGBAOtoAFiizq4JGsSnElAEXe1Ro4v3NOka69Mz8ZcOg5RYihJzXk2z
14+
IYAGl6/VCgxbZZEX1m1MDxPT+9N59ubWZXIntAQG9Roy5kdLxHI6yR+SAN74t5ee
15+
1910UkeO3Mcklu/ZK6j2q2NQo0A1k7UQDMKhQia67sdYXZsMTCBBCEBhAoGBAOoY
16+
+mLNngDCE62pqjWhCTZx28G3uYV+RAfILlmtO+WXfkeaWdNT2zCtqu2JA4+1Qa8p
17+
Hk3Q6h4dMlVxv7MRFfSt5MfNr2cXeSQMuF+RY92rYikZ+lEqQvx80OeWW4AnSFZw
18+
F2SzUIhMEKBpMioe0cVg97p8t6Y3SSEvUAON06IfAoGAfWGFXbBJWNSslzVNQaUx
19+
yecDrtbaLZYTKQivkn8+gdKup6ke/097dk8Ml7Bmi7JaZG9H3Cr4lN0Bcd+yK7zh
20+
h22yKbry7SNTF4I5fYXl/ZSsNhZO4sjNvLvp3wcufDr20YTgBjxriJg4e0iOBZAo
21+
27k8HJjP+QkNwjlIip1YocECgYAXxnGnlzp02UEmYhTnXXOSE/49rmspI6nFII+U
22+
jAOJTdmFbCezTtD0IK87cK7r6XzCTWrGfHFKnax/AQ53IUUZlI+nuzwVPE0RXDld
23+
e2apHL7bbcf+dkxXWFXfL2YbneG65ad6krubHB6ULBrsLDUh1rdSOVhtiS/kLunx
24+
cnYMAQKBgAh21WUGlxSYSCR6AO5nj7tmOsrohy4MkYQGK4LZ9FjKJA97M37hXaG2
25+
Bej7B+Smng1lLzXlpCBWpvoKlHcYT7R/aSg2SpV7lIeKqi526Qmne0gjbJRdNGJA
26+
p2xSAxrYL8KFvIlDX380SOFo2Ak2PbmFE9RukMuTN6k/g73fvLdY
2727
-----END RSA PRIVATE KEY-----

test/centos-image/etcd/ca.csr

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
11
-----BEGIN CERTIFICATE REQUEST-----
22
MIICeTCCAWECAQAwEjEQMA4GA1UEAxMHRXRjZCBDQTCCASIwDQYJKoZIhvcNAQEB
3-
BQADggEPADCCAQoCggEBAK4Z9AXbxUq/Nm3s/BO9+dRn54krfCqt1VW6ZPvNy+cE
4-
xYAGU9r63VPmvfit8YmXhpK7O0ZtEE5cB2fwcZ8KLaVtZusAAYZHTzQtiUnHGTq8
5-
bjAgOZsOxCk7X3t0Lis1cte9PdDhy7s85iRxExQGr5XZtZ4WgSyJsW/7g54t7Ka9
6-
tEFuqeNpXO5t2aVL/ZuePxZQcz+tNbE36do+N/rN6qFPs7kydGnizPen8spBAQOp
7-
wvcC32FJRJ8uo4P0R06rmdObc8nsVB9GUtXxkhGR0IQIZkQluSFecbGx31p0IGKM
8-
jvQEU4p++HtNbu0ET8SjpVjWx+n06uZByGyMTypMnJMCAwEAAaAiMCAGCSqGSIb3
9-
DQEJDjETMBEwDwYDVR0RBAgwBocEfwAAATANBgkqhkiG9w0BAQsFAAOCAQEAHyYl
10-
oibnkfrbmWV5AZ86RAv62RFQ2ZAY4gqsfAg9fAZAvAEXqz+xGyakPUuB/ab5BA75
11-
6/4Mpa4YTyVPnyRAEfswSa/p+rGbVcVCdLOXNCM+K9BOoRAB4uPqRb4ZDpFhAK1C
12-
ESYZpYqrSANQjejkr0Zfq1ZDIfQjfYlmUA0kM6h4CAoAlHhMgbpm4kCn6rfE0xbS
13-
IxONzEt9MwqRDXGQl1pHgiqxZ2LQcdQ1lxaDSg7yZPhjmo6LHueYBWRVUgW3mwxm
14-
Ga6P8RLEsrb/H5kp+q63P3X0Osv8II5uV2jpTb5vy0ucRAU84a+MJumVIn3O9HtH
15-
jkwpB7lj/U3NtvQw3Q==
3+
BQADggEPADCCAQoCggEBANdECE94WnKKdGJwMcKP71nSDPhVRw8OyMyOSqBrTCJF
4+
tGe2MmnR67v35zTxZoClgeFKHOkQXVaMY9kcXr1hC7A21sejPss/TSQbEAb4XxR1
5+
Rfhd+mP9BWKhneJEg7F3qXrkMHxt7uvB3w5rsHHkLkWJ3VCAsldkyXf3eW6EwpkK
6+
Hbx4roDIO8SobKeA9k8uzMCOdd+CSXOh4MRI9eyVzTsmB0jYt/qIvWAW4CZ5Erwy
7+
uAmFlckyYPUNHJs8tere8Kt9f9cQpUzxbTwSsg9ZUOtEAHwMmFU7JKMffJ/MCXsW
8+
hViHFlaUmyM62vJchKT7SOnbSdul7aywuxbXdJWwLb8CAwEAAaAiMCAGCSqGSIb3
9+
DQEJDjETMBEwDwYDVR0RBAgwBocEfwAAATANBgkqhkiG9w0BAQsFAAOCAQEArIfh
10+
ZksIKHFa7RYSR7Qr6OvD3UL6yRh5xx4BcNXGIWX/bM1THr5VFTLCyWnvMfXbx264
11+
EYJ9VyJPiFXer0VyWKWfcIYcGJAY8lEQhcAWkMb3Q+u0mX8otYt/ChqeSQIIWOJ5
12+
6Bi0FzvYxsneTPMXdTD6ZdHKrqVn8xUz6cbFhedyBwhxykvY/0vNov7Ad2+Sa2uK
13+
au+7r4iWQpaiETmCAAEVif72MXIqdPRYoUwm+rQuTgR9bZBwfcng1szKF7wf57lm
14+
s84nHgmB/CJ8p5tigqKDYRQB7HX3UI9KdKCB7xElg0tdwnD4rlRh+y+rWW7JFlwi
15+
BP6ku04mE8t3piEGbw==
1616
-----END CERTIFICATE REQUEST-----

0 commit comments

Comments
 (0)