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.
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.
$ sudo ./certbot-auto certonly --standalone -d test.100c1p43r.dev
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.100c1p43r.dev
Waiting for verification...
Cleaning up challenges
IMPORTANT NOTES:
- Congratulations! Your certificate and chain have been saved at:
/etc/letsencrypt/live/test.100c1p43r.dev/fullchain.pem
Your key file has been saved at:
/etc/letsencrypt/live/test.100c1p43r.dev/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.100c1p43r.dev/
$ 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.100c1p43r.dev
Issuer: CN=Let's Encrypt Authority X3, O=Let's Encrypt, C=US
Serial number: 3c9934c1ddb20d712ba893c7272c35349d4
Valid from: Thu Jan 11 20:41:39 CET 2018until: 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 6303 7D DD BA E6 D1 39 B7 A6 4565 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 68747470 3A 2F 2F 637073 2E 6C 6574 ..http://cps.let
0010: 7365 6E 6372797074 2E 6F 7267 sencrypt.org
], PolicyQualifierInfo: [
qualifierID: 1.3.6.1.5.5.7.2.2
qualifier: 0000: 3081 9E 0C 81 9B 54686973204365727469 0.....This Certi
0010: 66696361746520 6D 617920 6F 6E 6C 7920 ficate may only
0020: 6265207265 6C 696564207570 6F 6E 2062 be relied upon b
0030: 79205265 6C 7969 6E 6720506172746965 y Relying Partie
0040: 732061 6E 6420 6F 6E 6C 792069 6E 206163 s and only in ac
0050: 63 6F 726461 6E 63652077697468207468 cordance with th
0060: 652043657274696669636174652050 6F e Certificate Po
0070: 6C 6963792066 6F 75 6E 64206174206874 licy found at ht
0080: 747073 3A 2F 2F 6C 65747365 6E 63727970 tps://letsencryp
0090: 74 2E 6F 7267 2F 726570 6F 736974 6F 7279 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.100c1p43r.dev
]#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: a0141420000025385736a0b85eca708
Valid from: Thu Mar 17 17:40:46 CET 2016until: 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 9075 FF C4 15 .....,q...K.u...
0010: 60858910`...
]]#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: 162268747470 3A 2F 2F 637073 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:
$ 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
$ 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.