m0v server
Contents
Brief
A secure (tls) tcp server to receive status/meter readings with postgresql database to store readings. Server uses acme for auto-generation and renewal of certificates.
The server is one part of four:
- Data receipt server (this) -
locomov
- Data retrieval server -
finisher
- Device firmware -
provisioning_CC3220S_LAUNCHXL_tirtos_ccs
- Android app software -
SimpleLink_WiFi_Provisioning_Android_Source_2.2.257
Server
log
Stdout/stderr is piped to a modified version of funnel which writes and rotates log files.
./mov-server -stderrthreshold=INFO -secure -dbdb=m0v -dbuser=sridhar -dbpw= 2>&1 | funnel -app=mov &
./mov-confo-server -stderrthreshold=INFO -secure -dbdb=m0v -dbuser=sridhar -dbpw= 2>&1 | funnel -app=confo &
mov-confo-server has been deprecated by a handler in the data retrieval server
finisherbecause
mov-confo-serverconsisted of only a single https handler for the retrieval of the hash of a newly configured device using
device_nameand
ssid`.
data
A packet sent from a device (publisher/pub) consists of:
Id int64 `json:"id!`
Timestamp int64 `json:"timestamp,omitempty"`
Status bool `json:"status"`
Voltage float64 `json:"voltage"`
Frequency float64 `json:"freq"`
//Lat float64 `json:"lat"`
//Lng float64 `json:"lng"`
Id
(should be pub_id
) is the id of the pub which is set during configuration with app along with the location and other system info (tbc).
A packet saved to db is as follows with the Timestamp
from the packet used as created_at
:
pub_id INTEGER NOT NULL REFERENCES pub(pub_id),
id SERIAL PRIMARY KEY,
created_at TIMESTAMPTZ NOT NULL default current_timestamp,
saved_at TIMESTAMPTZ NOT NULL default current_timestamp,
voltage REAL,
frequency REAL,
protected boolean NOT NULL,
pub_id
is in the packet sent from a pub. id
is an incrementing id of the packet saved to db. created_at
is a timestamp
tls
Let's Encrypt uses DST as root (DST Root CA X3). This root CA (DST) is luckily in the list of common trusted root CAs used by the CC3220S. So download the root certificate and append it to the chain loaded by server like below (use it as b00m-trusted-root.pem).
Certificate chains are loaded leaf first. So create a certificate chain in the following order:
b00m-trusted-cert.pem + b00m-trusted-ca-cert.pem + b00m-trusted-root.pem = b00m-trusted-chain.pem
And load it like this:
b00m, err := tls.LoadX509KeyPair("b00m-trusted-chain.pem", "b00m-trusted-cert-key.pem")
// handle err
config := &tls.Config {
Certificate: []tls.Certificate{b00m},
...
}
The CC3220S which is a TCP client to this server only permits the use of RSA certificates and keys. The acme default is now ec (elliptic curve). So to force acme to issue a rsa certificate, create a transport with tls.TLS_RSA_WITH_AES_256_CBC_SHA
as the only CipherSuite in the tls.Config and use that transport with a client used to call the server which uses acme. Acme will then on-the-fly produce a RSA certificate (b00m.in+rsa).
tr := &http.Transport{
TLSClientConfig: &tls.Config{
CipherSuites: []uint16{tls.TLS_RSA_WITH_AES_256_CBC_SHA}
}}
Test client
The client in b00m_client.go
is a Go TCP client which connects to the TCP server with tls.Config{InsecureSkipVerify:false}
to verify server certificate. It then posts a json packet and receives a Thank you
reply from the server.
Cloud Confirmation
A second simple tcp+ssl server listening on another port (38979) provides the cloud confirmation to the Android (external configuration) app.
The server accepts tokens from successfully provisioned devices and returns them (when queried) to the external configuration app which generated the tokens in the first place.
- The app connects to the cc3220s and provisions the device with ssid, password and a token.
- The device then connects to the cloud confirmation server and posts the token to confirm successful provisioning.
- The app then queries the cloud confirmation server for the token, upon receiving which the configuratiaon process is complete.
type Confo struct {
Devicename string`json:"deviceName"`
Timestamp int64 `json:"timestamp,omitempty"`
Ssid string `json:"ssid"`
Hash int64 `json:"hash,omitempty"`
Sub string `json:"email,omitempty"`
}
On receiving a confo, the server does the following:
- check if Sub
is known, i.e. registered email in sub
- yes: check if Hash
exists in pub
- yes: check if Sub.Id
== creator
in pub
- yes - ok save the confo in confo
as the sub
and pub
hash pre-exist.
- no - conflict! save the confo in cconfo
(will mean 2 subs have configured same Hash
which is an unlikely event, sub will have to be informed to change devicename)
- other - if creator
in Pub
is unpopulated, populate it with Sub.Id
- no: create an entry in pub
and save the confo in confo
- no: save the email in csub
table. check if Hash
exists in pub
- yes: ensure that Sub.Id
!= creator
in pub
- yes - ok save the confo
- no - we've a problem - see other below. unpopulate creator
in pub
- other - if creator
in Pub
is unpopulated, it's left unpopulated (only registered emails in sub
can be creator
)
The confo gets saved anyway - either in confo
or cconfo
Gotchas
The following files are for obvious reasons not part of the respository:
b00m.in+rsa
b000m-trusted.chain.pem
b00m-trusted-key.pem
Certificate problems between client/server will usually result in an error on the server such as:
Error decoding JSON data tls: no cipher suite supported by both client and server