Setting environment for Messenger bot

Recently I decided to move my pet project further on and create messenger bot for setting hook on fully booked crossfit workouts and getting notification when vacancy occurs. It’s not rocket science, but required some effort, which I thought would be nice to document here.

To write your own Messenger bot you need to set a web server and get trusted SSL certificate. And this note could be easily ended if I used Heroku┬áto host my application, but I decided to get my hands a little dirty and try myself with own VPS. To do so I had to create my own trusted SSL certificate. I didn’t want to pay, so decided to use Let’s Encrypt which offers full chain of trust, which is required with most browsers as well as with Facebook’s curl command call.

Getting certificate was quite easy. You just need to download Certbot application and proof on your machine, that it is you, who control the domain.

1
2
$ wget https://dl.eff.org/certbot-auto
$ chmod a+x certbot-auto

And then using standalone option I generated certificate. To do so I needed to switch off apache service for a moment, because in this mode Certbot is creating own web server to proof that domain is assigned properly.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
$ sudo ./certbot-auto certonly --standalone  -d test.dusik.pl
Saving debug log to /var/log/letsencrypt/letsencrypt.log
Plugins selected: Authenticator standalone, Installer None
Obtaining a new certificate
Performing the following challenges:
http-01 challenge for test.dusik.pl
Waiting for verification...
Cleaning up challenges
 
IMPORTANT NOTES:
 - Congratulations! Your certificate and chain have been saved at:
   /etc/letsencrypt/live/test.dusik.pl/fullchain.pem
   Your key file has been saved at:
   /etc/letsencrypt/live/test.dusik.pl/privkey.pem
   Your cert will expire on 2018-04-11. To obtain a new or tweaked
   version of this certificate in the future, simply run certbot-auto
   again. To non-interactively renew *all* of your certificates, run
   "certbot-auto renew"
 - If you like Certbot, please consider supporting our work by:
 
   Donating to ISRG / Let's Encrypt:   https://letsencrypt.org/donate
   Donating to EFF:                    https://eff.org/donate-le

After that my certificate resides in /etc/letsencrypt/archive/test.dusik.pl/

1
2
3
4
5
6
$ ll
total 16
-rw-r--r-- 1 root root 1789 01-11 21:41 cert1.pem
-rw-r--r-- 1 root root 1647 01-11 21:41 chain1.pem
-rw-r--r-- 1 root root 3436 01-11 21:41 fullchain1.pem
-rw-r--r-- 1 root root 1704 01-11 21:41 privkey1.pem

To make it usable by web server I needed to export it into p12 cert and then move it into keystore

1
2
3
$ sudo openssl pkcs12 -export -in fullchain1.pem -inkey privkey1.pem -name tomcat -out cert.p12
Enter Export Password: ****
Verifying - Enter Export Password: ****
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
$ sudo keytool -importkeystore -destkeystore keystore.jks -srckeystore cert.p12 -srcstoretype PKCS12
Enter destination keystore password: ****
Re-enter new password: ****
Enter source keystore password:  
Entry for alias tomcat successfully imported.
Import command completed:  1 entries successfully imported, 0 entries failed or cancelled
 
$ sudo /opt/jdk1.8.0_65/bin/keytool -list -v -keystore keystore.jks
Enter keystore password: ****
 
Keystore type: JKS
Keystore provider: SUN
 
Your keystore contains 1 entry
 
Alias name: tomcat
Creation date: Jan 11, 2018
Entry type: PrivateKeyEntry
Certificate chain length: 2
Certificate[1]:
Owner: CN=test.dusik.pl
Issuer: CN=Let's Encrypt Authority X3, O=Let's Encrypt, C=US
Serial number: 3c9934c0ddb20d712ba893c7272c35349d4
Valid from: Thu Jan 11 20:41:39 CET 2018 until: Wed Apr 11 21:41:39 CEST 2018
Certificate fingerprints:
	 MD5:  01:A2:59:B3:4A:E4:AE:D2:79:B5:BD:21:A7:63:BA:35
	 SHA1: 37:D3:8B:FB:C1:C5:7F:D1:5F:14:9A:99:7A:37:8C:1B:E3:EE:3F:DE
	 SHA256: 2F:3B:EA:30:87:9D:C6:90:53:7E:F8:2B:7B:07:46:A1:19:03:FA:7B:40:82:4C:1E:9A:8B:76:D7:30:DE:AE:F0
	 Signature algorithm name: SHA256withRSA
	 Version: 3
 
Extensions: 
 
#1: ObjectId: 1.3.6.1.5.5.7.1.1 Criticality=false
AuthorityInfoAccess [
  [
   accessMethod: ocsp
   accessLocation: URIName: http://ocsp.int-x3.letsencrypt.org
, 
   accessMethod: caIssuers
   accessLocation: URIName: http://cert.int-x3.letsencrypt.org/
]
]
 
#2: ObjectId: 2.5.29.35 Criticality=false
AuthorityKeyIdentifier [
KeyIdentifier [
0000: A8 4A 6A 63 04 7D DD BA   E6 D1 39 B7 A6 45 65 EF  .Jjc......9..Ee.
0010: F3 A8 EC A1                                        ....
]
]
 
#3: ObjectId: 2.5.29.19 Criticality=true
BasicConstraints:[
  CA:false
  PathLen: undefined
]
 
#4: ObjectId: 2.5.29.32 Criticality=false
CertificatePolicies [
  [CertificatePolicyId: [2.23.140.1.2.1]
[]  ]
  [CertificatePolicyId: [1.3.6.1.4.1.44947.1.1.1]
[PolicyQualifierInfo: [
  qualifierID: 1.3.6.1.5.5.7.2.1
  qualifier: 0000: 16 1A 68 74 74 70 3A 2F   2F 63 70 73 2E 6C 65 74  ..http://cps.let
0010: 73 65 6E 63 72 79 70 74   2E 6F 72 67              sencrypt.org
 
], PolicyQualifierInfo: [
  qualifierID: 1.3.6.1.5.5.7.2.2
  qualifier: 0000: 30 81 9E 0C 81 9B 54 68   69 73 20 43 65 72 74 69  0.....This Certi
0010: 66 69 63 61 74 65 20 6D   61 79 20 6F 6E 6C 79 20  ficate may only 
0020: 62 65 20 72 65 6C 69 65   64 20 75 70 6F 6E 20 62  be relied upon b
0030: 79 20 52 65 6C 79 69 6E   67 20 50 61 72 74 69 65  y Relying Partie
0040: 73 20 61 6E 64 20 6F 6E   6C 79 20 69 6E 20 61 63  s and only in ac
0050: 63 6F 72 64 61 6E 63 65   20 77 69 74 68 20 74 68  cordance with th
0060: 65 20 43 65 72 74 69 66   69 63 61 74 65 20 50 6F  e Certificate Po
0070: 6C 69 63 79 20 66 6F 75   6E 64 20 61 74 20 68 74  licy found at ht
0080: 74 70 73 3A 2F 2F 6C 65   74 73 65 6E 63 72 79 70  tps://letsencryp
0090: 74 2E 6F 72 67 2F 72 65   70 6F 73 69 74 6F 72 79  t.org/repository
00A0: 2F                                                 /
 
]]  ]
]
 
#5: ObjectId: 2.5.29.37 Criticality=false
ExtendedKeyUsages [
  serverAuth
  clientAuth
]
 
#6: ObjectId: 2.5.29.15 Criticality=true
KeyUsage [
  DigitalSignature
  Key_Encipherment
]
 
#7: ObjectId: 2.5.29.17 Criticality=false
SubjectAlternativeName [
  DNSName: test.dusik.pl
]
 
#8: ObjectId: 2.5.29.14 Criticality=false
SubjectKeyIdentifier [
KeyIdentifier [
0000: 3C FA C9 A8 07 A5 EC A3   61 C7 AA B0 3F E8 D8 4D  <.......a...?..M
0010: 04 9D 74 A6                                        ..t.
]
]
 
Certificate[2]:
Owner: CN=Let's Encrypt Authority X3, O=Let's Encrypt, C=US
Issuer: CN=DST Root CA X3, O=Digital Signature Trust Co.
Serial number: a0141420000015385736a0b85eca708
Valid from: Thu Mar 17 17:40:46 CET 2016 until: Wed Mar 17 17:40:46 CET 2021
Certificate fingerprints:
	 MD5:  B1:54:09:27:4F:54:AD:8F:02:3D:3B:85:A5:EC:EC:5D
	 SHA1: E6:A3:B4:5B:06:2D:50:9B:33:82:28:2D:19:6E:FE:97:D5:95:6C:CB
	 SHA256: 25:84:7D:66:8E:B4:F0:4F:DD:40:B1:2B:6B:07:40:C5:67:DA:7D:02:43:08:EB:6C:2C:96:FE:41:D9:DE:21:8D
	 Signature algorithm name: SHA256withRSA
	 Version: 3
 
Extensions: 
 
#1: ObjectId: 1.3.6.1.5.5.7.1.1 Criticality=false
AuthorityInfoAccess [
  [
   accessMethod: ocsp
   accessLocation: URIName: http://isrg.trustid.ocsp.identrust.com
, 
   accessMethod: caIssuers
   accessLocation: URIName: http://apps.identrust.com/roots/dstrootcax3.p7c
]
]
 
#2: ObjectId: 2.5.29.35 Criticality=false
AuthorityKeyIdentifier [
KeyIdentifier [
0000: C4 A7 B1 A4 7B 2C 71 FA   DB E1 4B 90 75 FF C4 15  .....,q...K.u...
0010: 60 85 89 10                                        `...
]
]
 
#3: ObjectId: 2.5.29.19 Criticality=true
BasicConstraints:[
  CA:true
  PathLen:0
]
 
#4: ObjectId: 2.5.29.31 Criticality=false
CRLDistributionPoints [
  [DistributionPoint:
     [URIName: http://crl.identrust.com/DSTROOTCAX3CRL.crl]
]]
 
#5: ObjectId: 2.5.29.32 Criticality=false
CertificatePolicies [
  [CertificatePolicyId: [2.23.140.1.2.1]
[]  ]
  [CertificatePolicyId: [1.3.6.1.4.1.44947.1.1.1]
[PolicyQualifierInfo: [
  qualifierID: 1.3.6.1.5.5.7.2.1
  qualifier: 0000: 16 22 68 74 74 70 3A 2F   2F 63 70 73 2E 72 6F 6F  ."http://cps.roo
0010: 74 2D 78 31 2E 6C 65 74   73 65 6E 63 72 79 70 74  t-x1.letsencrypt
0020: 2E 6F 72 67                                        .org
 
]]  ]
]
 
#6: ObjectId: 2.5.29.15 Criticality=true
KeyUsage [
  DigitalSignature
  Key_CertSign
  Crl_Sign
]
 
#7: ObjectId: 2.5.29.14 Criticality=false
SubjectKeyIdentifier [
KeyIdentifier [
0000: A8 4A 6A 63 04 7D DD BA   E6 D1 39 B7 A6 45 65 EF  .Jjc......9..Ee.
0010: F3 A8 EC A1                                        ....
]
]
 
 
 
*******************************************
*******************************************

Above keystore is ready to be imported into tomcat container. All we need to do now is have open port for our application to communicate. Because I’m using Apache server to host this blog, I had two options for that. Either configure reverse proxy mod for Apache or open new port on firewall. I choosed the other option, because I didn’t want to rely on Apache server. I wanted the port to be 4321, because why not. But this port wasn’t reachable from outside, so I needed to do some iptables tweaking:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
$ sudo iptables-save
# Generated by iptables-save v1.4.7 on Thu Jan 11 22:43:28 2018
*filter
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [1039644:427395441]
-A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT 
-A INPUT -p icmp -j ACCEPT 
-A INPUT -i lo -j ACCEPT 
-A INPUT -p tcp -m state --state NEW -m tcp --dport 22 -j ACCEPT 
-A INPUT -p tcp -m state --state NEW -m tcp --dport 80 -j ACCEPT 
-A INPUT -p tcp -m state --state NEW -m tcp --dport 10000 -j ACCEPT 
-A INPUT -p tcp -m state --state NEW -m tcp --dport 21 -j ACCEPT 
-A INPUT -p tcp -m state --state NEW -m tcp --dport 20 -j ACCEPT 
-A INPUT -p tcp -m state --state NEW -m tcp --dport 35000:35999 --tcp-flags FIN,SYN,RST,ACK SYN -j ACCEPT 
-A INPUT -p tcp -m state --state NEW -m tcp --dport 443 -j ACCEPT 
-A INPUT -j REJECT --reject-with icmp-host-prohibited 
-A FORWARD -j REJECT --reject-with icmp-host-prohibited 
COMMIT
# Completed on Thu Jan 11 22:43:28 2018

This is standard VPS configuration. Ports like HTTP, SSH, HTTPS are opened, but all other are blocked with command

1
-A FORWARD -j REJECT --reject-with icmp-host-prohibited

What we need here is add new ACCEPT rule before REJECT one. We use following command to print line numbers for us

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
$ sudo iptables -L --line-numbers
Chain INPUT (policy ACCEPT)
num  target     prot opt source               destination         
1    ACCEPT     all  --  anywhere             anywhere            state RELATED,ESTABLISHED 
2    ACCEPT     icmp --  anywhere             anywhere            
3    ACCEPT     all  --  anywhere             anywhere            
4    ACCEPT     tcp  --  anywhere             anywhere            state NEW tcp dpt:ssh 
5    ACCEPT     tcp  --  anywhere             anywhere            state NEW tcp dpt:http 
6    ACCEPT     tcp  --  anywhere             anywhere            state NEW tcp dpt:ndmp 
7    ACCEPT     tcp  --  anywhere             anywhere            state NEW tcp dpt:ftp 
8    ACCEPT     tcp  --  anywhere             anywhere            state NEW tcp dpt:ftp-data 
9    ACCEPT     tcp  --  anywhere             anywhere            state NEW tcp dpts:35000:35999 flags:FIN,SYN,RST,ACK/SYN 
10   ACCEPT     tcp  --  anywhere             anywhere            state NEW tcp dpt:https 
12   REJECT     all  --  anywhere             anywhere            reject-with icmp-host-prohibited 
 
Chain FORWARD (policy ACCEPT)
num  target     prot opt source               destination         
1    REJECT     all  --  anywhere             anywhere            reject-with icmp-host-prohibited 
 
Chain OUTPUT (policy ACCEPT)
num  target     prot opt source               destination

Than remove REJECT rule with

1
$ sudo iptables -D INPUT 12

Add new ACCEPT RULE and add REJECT rule at the end:

1
2
$ sudo iptables -A INPUT -p tcp -m state --state NEW -m tcp --dport 4321 -j ACCEPT 
$ sudo iptables -A FORWARD -j REJECT --reject-with icmp-host-prohibited

And that’s it. I created trusted certificate and opened port on firewall. Now there is time to write an application, but I will cover that in another post.

Leave a Reply

Your email address will not be published. Required fields are marked *