Message ID | 1512508319-30950-1-git-send-email-ayoub.zaki@embexus.com |
---|---|
State | Changes Requested |
Headers | show |
Series | hawkbit: adding authentication using security token | expand |
Hi Ayoub, On Tue, Dec 5, 2017 at 10:11 PM, Ayoub Zaki <ayoub.zaki@embexus.com> wrote: > When a target is created within hawkBit a specific security token (32 > alphanumeric character) is generated. > > This can be used to authenticate the target through a HTTP-Authorization > header with a custom scheme TargetToken. > <code-snip> > This is a nice addition, but I have a couple of notes about it. 1. Security Token types I'd rather call the variables / parameters with the whole name "target_token" rather than just "token", as hawkBit supports two different security tokens: GatewayToken and TargetToken: http://www.eclipse.org/hawkbit/documentation/security/security.html You could even add support for GatewayToken straight away, as the header is the same ("Authorization"), just the content / schema is different. The only thing needed is to provide two separate parameters, e.g.: target_token = "3bc13b476cb3962a0c63a5c92beacfh7"; gateway_token = "3nkswAZhX81oDtktq0FF9Pn0Tc0UGXPW"; If you don't want to implement support for the GatewayToken I can do it after your patch, but just be specific in the variable name, don't use just "token". 2. TargetToken loading One thing to keep in mind with this implementation is that: 1. the target needs to first connect anonymously to "plug-and-play" and be assigned a TargetToken 2. the TargetToken needs to be manually inserted in the swupdate.cfg or startup parameters 3. swupdate daemon needs to be restarted This procedure works for a few devices, but doesn't scale well for lots of devices (or is at least tedious). So thank you very much for your patch Ayoub, let me know if you want to proceed with implementing the GatewayToken or you want a helping hand. I hope this feedback is appreciated, waiting for Stefano's opinion. Bests, Diego Rondini Sr. Embedded Engineer Kynetics www.kynetics.com
Hi Diego, On 06.12.2017 10:50, Diego Rondini wrote: > Hi Ayoub, > > On Tue, Dec 5, 2017 at 10:11 PM, Ayoub Zaki <ayoub.zaki@embexus.com > <mailto:ayoub.zaki@embexus.com>> wrote: > > When a target is created within hawkBit a specific security token > (32 alphanumeric character) is generated. > > This can be used to authenticate the target through a > HTTP-Authorization header with a custom scheme TargetToken. > <code-snip> > > > This is a nice addition, but I have a couple of notes about it. Thanks ! > > 1. Security Token types > I'd rather call the variables / parameters with the whole name > "target_token" rather than just "token", as hawkBit supports two > different security tokens: GatewayToken and TargetToken: > http://www.eclipse.org/hawkbit/documentation/security/security.html > You could even add support for GatewayToken straight away, as the > header is the same ("Authorization"), just the content / schema is > different. > The only thing needed is to provide two separate parameters, e.g.: > target_token = "3bc13b476cb3962a0c63a5c92beacfh7"; > gateway_token = "3nkswAZhX81oDtktq0FF9Pn0Tc0UGXPW"; > If you don't want to implement support for the GatewayToken I can do > it after your patch, but just be specific in the variable name, don't > use just "token". I didn't use GW Token yet but it seens to be straight forward to implement, yes of course you can send a patch :-) > > 2. TargetToken loading > One thing to keep in mind with this implementation is that: > 1. the target needs to first connect anonymously to "plug-and-play" > and be assigned a TargetToken Not necessarly, the target can be created using the management API before actuallly the real device connects to Hawkbit : [1] https://docs.bosch-iot-rollouts.com/documentation/developerguide/apispecifications/managementapi/targets.html The security token is then generated and and can be put on the real device during the production for example. Although I prefer using a ssl based authentication using certificates ! > 2. the TargetToken needs to be manually inserted in the swupdate.cfg > or startup parameters > 3. swupdate daemon needs to be restarted > This procedure works for a few devices, but doesn't scale well for > lots of devices (or is at least tedious). see above ! swupdate.cfg should be specific to each device : see identify section in config file, so the best is perform this customization at production time. > > So thank you very much for your patch Ayoub, let me know if you want > to proceed with implementing the GatewayToken or you want a helping hand. > > I hope this feedback is appreciated, waiting for Stefano's opinion. You're welcome. Best regards,
Hi Ayoub, On Wed, Dec 6, 2017 at 2:02 PM, Ayoub Zaki <ayoub.zaki@embexus.com> wrote: > <snip> > > >> 1. Security Token types >> I'd rather call the variables / parameters with the whole name >> "target_token" rather than just "token", as hawkBit supports two different >> security tokens: GatewayToken and TargetToken: >> http://www.eclipse.org/hawkbit/documentation/security/security.html >> You could even add support for GatewayToken straight away, as the header >> is the same ("Authorization"), just the content / schema is different. >> The only thing needed is to provide two separate parameters, e.g.: >> target_token = "3bc13b476cb3962a0c63a5c92beacfh7"; >> gateway_token = "3nkswAZhX81oDtktq0FF9Pn0Tc0UGXPW"; >> If you don't want to implement support for the GatewayToken I can do it >> after your patch, but just be specific in the variable name, don't use just >> "token". >> > > I didn't use GW Token yet but it seens to be straight forward to > implement, yes of course you can send a patch :-) > Sure I will. Let us know if you plan to change the name of the "token" var / parameter. > > >> 2. TargetToken loading >> One thing to keep in mind with this implementation is that: >> 1. the target needs to first connect anonymously to "plug-and-play" and >> be assigned a TargetToken >> > > Not necessarly, the target can be created using the management API before > actuallly the real device connects to Hawkbit : > > [1] https://docs.bosch-iot-rollouts.com/documentation/developerg > uide/apispecifications/managementapi/targets.html > > The security token is then generated and and can be put on the real device > during the production for example. > Sure, hawkBit has the ability to preload the targets. > > Although I prefer using a ssl based authentication using certificates ! > > > 2. the TargetToken needs to be manually inserted in the swupdate.cfg or >> startup parameters >> 3. swupdate daemon needs to be restarted >> This procedure works for a few devices, but doesn't scale well for lots >> of devices (or is at least tedious). >> > > see above ! > > swupdate.cfg should be specific to each device : see identify section in > config file, so the best is perform this customization at production time. > Right, as you say, you still need to load the unique TargetToken to each device before it is able to connect anyhow. Bests, Diego Rondini Sr. Embedded Engineer Kynetics www.kynetics.com
Hi Ayoub, On 05/12/2017 22:11, Ayoub Zaki wrote: > When a target is created within hawkBit a specific security token (32 alphanumeric character) is generated. > > This can be used to authenticate the target through a HTTP-Authorization header with a custom scheme TargetToken. > --- > corelib/channel_curl.c | 12 ++++++++++++ > examples/configuration/swupdate.cfg | 3 +++ > include/channel_curl.h | 1 + > suricatta/server_hawkbit.c | 3 +++ > 4 files changed, 19 insertions(+) > > diff --git a/corelib/channel_curl.c b/corelib/channel_curl.c > index 608f5d3..ec412c0 100644 > --- a/corelib/channel_curl.c > +++ b/corelib/channel_curl.c > @@ -345,6 +345,7 @@ channel_op_res_t channel_set_options(channel_t *this, > { > channel_curl_t *channel_curl = this->priv; > channel_op_res_t result = CHANNEL_OK; > + char *token = NULL; > if ((curl_easy_setopt(channel_curl->handle, CURLOPT_URL, > channel_data->url) != CURLE_OK) || > (curl_easy_setopt(channel_curl->handle, CURLOPT_USERAGENT, > @@ -397,6 +398,17 @@ channel_op_res_t channel_set_options(channel_t *this, > } > } > > + if (channel_data->token != NULL) { > + if (asprintf(&token, "Authorization: TargetToken %s", > + channel_data->token)) { > + if (((channel_curl->header = curl_slist_append( > + channel_curl->header, token)) == NULL)) { > + result = CHANNEL_EINIT; > + goto cleanup; > + } > + } > + } > + After I moved the curl code outside suricatta / hawkbit, this part is completely unaware abot the type of connection. It is simply a JSON over http(s), but without any specific server detail. That means that the tokens should be integrated in the server part and simply passed to the channel curl. channel_curl should not know that a "Authorization: TargetToken" must be sent, because this is Hawkbit pecific. A different backend will ask for a different header. > switch (method) { > case CHANNEL_GET: > if (curl_easy_setopt(channel_curl->handle, CURLOPT_CUSTOMREQUEST, > diff --git a/examples/configuration/swupdate.cfg b/examples/configuration/swupdate.cfg > index f9366fd..0d4aba2 100644 > --- a/examples/configuration/swupdate.cfg > +++ b/examples/configuration/swupdate.cfg > @@ -101,6 +101,8 @@ identify : ( > # path of the file containing the key for ssl connection > # sslcert : string > # path of the file containing the certificate for SSL connection > +# token : string > +# Hawkbit security token > # proxy : string > # in case the server is reached via a proxy > > @@ -122,6 +124,7 @@ suricatta : > cafile = "/etc/ssl/cafile"; > sslkey = "/etc/ssl/sslkey"; > sslcert = "/etc/ssl/sslcert"; > + token = "3bc13b476cb3962a0c63a5c92beacfh7"; > */ > }; > > diff --git a/include/channel_curl.h b/include/channel_curl.h > index 98240a9..156d671 100644 > --- a/include/channel_curl.h > +++ b/include/channel_curl.h > @@ -46,6 +46,7 @@ typedef struct { > char *cafile; > char *sslkey; > char *sslcert; > + char *token; > char *proxy; > char *info; > unsigned int retry_sleep; > diff --git a/suricatta/server_hawkbit.c b/suricatta/server_hawkbit.c > index 175396c..ce5374b 100644 > --- a/suricatta/server_hawkbit.c > +++ b/suricatta/server_hawkbit.c > @@ -1527,6 +1527,9 @@ static int suricatta_settings(void *elem, void __attribute__ ((__unused__)) *da > GET_FIELD_STRING_RESET(LIBCFG_PARSER, elem, "proxy", tmp); > if (strlen(tmp)) > SETSTRING(channel_data_defaults.proxy, tmp); > + GET_FIELD_STRING_RESET(LIBCFG_PARSER, elem, "token", tmp); > + if (strlen(tmp)) > + SETSTRING(channel_data_defaults.token, tmp); > > return 0; > > Best regards, Stefano Babic
Hi Diego, On 06/12/2017 10:50, Diego Rondini wrote: > Hi Ayoub, > > On Tue, Dec 5, 2017 at 10:11 PM, Ayoub Zaki <ayoub.zaki@embexus.com > <mailto:ayoub.zaki@embexus.com>> wrote: > > When a target is created within hawkBit a specific security token > (32 alphanumeric character) is generated. > > This can be used to authenticate the target through a > HTTP-Authorization header with a custom scheme TargetToken. > <code-snip> > > > This is a nice addition, but I have a couple of notes about it. > > 1. Security Token types > I'd rather call the variables / parameters with the whole name > "target_token" rather than just "token", as hawkBit supports two > different security tokens: GatewayToken and TargetToken: > http://www.eclipse.org/hawkbit/documentation/security/security.html > You could even add support for GatewayToken straight away, as the header > is the same ("Authorization"), just the content / schema is different. > The only thing needed is to provide two separate parameters, e.g.: > target_token = "3bc13b476cb3962a0c63a5c92beacfh7"; > gateway_token = "3nkswAZhX81oDtktq0FF9Pn0Tc0UGXPW"; It is also to think about how these parameters are put into the configuration file. Tenant-id are often derived from some device specific data (serial number, etc.) and passed to SWUpdate at start up. Tokens (target token) are known when target connects for the first time. I guess we needalso the way to store back the token after first connection. > If you don't want to implement support for the GatewayToken I can do it > after your patch, but just be specific in the variable name, don't use > just "token". > > 2. TargetToken loading > One thing to keep in mind with this implementation is that: > 1. the target needs to first connect anonymously to "plug-and-play" and > be assigned a TargetToken > 2. the TargetToken needs to be manually inserted in the swupdate.cfg or > startup parameters > 3. swupdate daemon needs to be restarted > This procedure works for a few devices, but doesn't scale well for lots > of devices (or is at least tedious). This just works during the development. On large scale, a certificate based mechanism is much more safe as this one with tokens. I will expect that tokens are already set for all devices with MKMNT interface or they are free to have and the devices store permanently the token the first time they connect. > > So thank you very much for your patch Ayoub, let me know if you want to > proceed with implementing the GatewayToken or you want a helping hand. > > I hope this feedback is appreciated, waiting for Stefano's opinion. > Best regards, Stefano
Hi Stefano, On 06.12.2017 14:57, Stefano Babic wrote: > Hi Ayoub, > > On 05/12/2017 22:11, Ayoub Zaki wrote: >> When a target is created within hawkBit a specific security token (32 alphanumeric character) is generated. >> >> This can be used to authenticate the target through a HTTP-Authorization header with a custom scheme TargetToken. >> --- >> corelib/channel_curl.c | 12 ++++++++++++ >> examples/configuration/swupdate.cfg | 3 +++ >> include/channel_curl.h | 1 + >> suricatta/server_hawkbit.c | 3 +++ >> 4 files changed, 19 insertions(+) >> >> diff --git a/corelib/channel_curl.c b/corelib/channel_curl.c >> index 608f5d3..ec412c0 100644 >> --- a/corelib/channel_curl.c >> +++ b/corelib/channel_curl.c >> @@ -345,6 +345,7 @@ channel_op_res_t channel_set_options(channel_t *this, >> { >> channel_curl_t *channel_curl = this->priv; >> channel_op_res_t result = CHANNEL_OK; >> + char *token = NULL; >> if ((curl_easy_setopt(channel_curl->handle, CURLOPT_URL, >> channel_data->url) != CURLE_OK) || >> (curl_easy_setopt(channel_curl->handle, CURLOPT_USERAGENT, >> @@ -397,6 +398,17 @@ channel_op_res_t channel_set_options(channel_t *this, >> } >> } >> >> + if (channel_data->token != NULL) { >> + if (asprintf(&token, "Authorization: TargetToken %s", >> + channel_data->token)) { >> + if (((channel_curl->header = curl_slist_append( >> + channel_curl->header, token)) == NULL)) { >> + result = CHANNEL_EINIT; >> + goto cleanup; >> + } >> + } >> + } >> + > After I moved the curl code outside suricatta / hawkbit, this part is > completely unaware abot the type of connection. It is simply a JSON over > http(s), but without any specific server detail. > > That means that the tokens should be integrated in the server part and > simply passed to the channel curl. channel_curl should not know that a > "Authorization: TargetToken" must be sent, because this is Hawkbit > pecific. A different backend will ask for a different header. I agree token parameter is very specific to hawbit: it should belong to server_hawkbit_t structure and passed to channel url. I will rework the patch, test it and send a new version. > >> switch (method) { >> case CHANNEL_GET: >> if (curl_easy_setopt(channel_curl->handle, CURLOPT_CUSTOMREQUEST, >> diff --git a/examples/configuration/swupdate.cfg b/examples/configuration/swupdate.cfg >> index f9366fd..0d4aba2 100644 >> --- a/examples/configuration/swupdate.cfg >> +++ b/examples/configuration/swupdate.cfg >> @@ -101,6 +101,8 @@ identify : ( >> # path of the file containing the key for ssl connection >> # sslcert : string >> # path of the file containing the certificate for SSL connection >> +# token : string >> +# Hawkbit security token >> # proxy : string >> # in case the server is reached via a proxy >> >> @@ -122,6 +124,7 @@ suricatta : >> cafile = "/etc/ssl/cafile"; >> sslkey = "/etc/ssl/sslkey"; >> sslcert = "/etc/ssl/sslcert"; >> + token = "3bc13b476cb3962a0c63a5c92beacfh7"; >> */ >> }; >> >> diff --git a/include/channel_curl.h b/include/channel_curl.h >> index 98240a9..156d671 100644 >> --- a/include/channel_curl.h >> +++ b/include/channel_curl.h >> @@ -46,6 +46,7 @@ typedef struct { >> char *cafile; >> char *sslkey; >> char *sslcert; >> + char *token; >> char *proxy; >> char *info; >> unsigned int retry_sleep; >> diff --git a/suricatta/server_hawkbit.c b/suricatta/server_hawkbit.c >> index 175396c..ce5374b 100644 >> --- a/suricatta/server_hawkbit.c >> +++ b/suricatta/server_hawkbit.c >> @@ -1527,6 +1527,9 @@ static int suricatta_settings(void *elem, void __attribute__ ((__unused__)) *da >> GET_FIELD_STRING_RESET(LIBCFG_PARSER, elem, "proxy", tmp); >> if (strlen(tmp)) >> SETSTRING(channel_data_defaults.proxy, tmp); >> + GET_FIELD_STRING_RESET(LIBCFG_PARSER, elem, "token", tmp); >> + if (strlen(tmp)) >> + SETSTRING(channel_data_defaults.token, tmp); >> >> return 0; >> >> Best regards,
Hi Stefano and all, On Wed, Dec 6, 2017 at 2:57 PM, Stefano Babic <sbabic@denx.de> wrote: > Hi Ayoub, > > On 05/12/2017 22:11, Ayoub Zaki wrote: >> When a target is created within hawkBit a specific security token (32 alphanumeric character) is generated. >> >> This can be used to authenticate the target through a HTTP-Authorization header with a custom scheme TargetToken. >> --- >> corelib/channel_curl.c | 12 ++++++++++++ >> examples/configuration/swupdate.cfg | 3 +++ >> include/channel_curl.h | 1 + >> suricatta/server_hawkbit.c | 3 +++ >> 4 files changed, 19 insertions(+) >> >> diff --git a/corelib/channel_curl.c b/corelib/channel_curl.c >> index 608f5d3..ec412c0 100644 >> --- a/corelib/channel_curl.c >> +++ b/corelib/channel_curl.c >> @@ -345,6 +345,7 @@ channel_op_res_t channel_set_options(channel_t *this, >> { >> channel_curl_t *channel_curl = this->priv; >> channel_op_res_t result = CHANNEL_OK; >> + char *token = NULL; >> if ((curl_easy_setopt(channel_curl->handle, CURLOPT_URL, >> channel_data->url) != CURLE_OK) || >> (curl_easy_setopt(channel_curl->handle, CURLOPT_USERAGENT, >> @@ -397,6 +398,17 @@ channel_op_res_t channel_set_options(channel_t *this, >> } >> } >> >> + if (channel_data->token != NULL) { >> + if (asprintf(&token, "Authorization: TargetToken %s", >> + channel_data->token)) { >> + if (((channel_curl->header = curl_slist_append( >> + channel_curl->header, token)) == NULL)) { >> + result = CHANNEL_EINIT; >> + goto cleanup; >> + } >> + } >> + } >> + > > After I moved the curl code outside suricatta / hawkbit, this part is > completely unaware abot the type of connection. It is simply a JSON over > http(s), but without any specific server detail. > > That means that the tokens should be integrated in the server part and > simply passed to the channel curl. channel_curl should not know that a > "Authorization: TargetToken" must be sent, because this is Hawkbit > pecific. A different backend will ask for a different header. > Sorry to resurrect this thread, but I'm about to send a reworked version of this patch moving the definition of the specific header in the hawkbit server specific code. I'm unsure if the approach I've taken is what you Stefano had in mind, as the channel_data_t struct in channel_curl.h still has an addition. In this case though I've added a generic "header" rather than a hawkBit-specific "token". If this is not what you wanted, please elaborate more about your idea, as the curl header list is defined in the channel_curl_t as part of channel_curl.c, so not accessible from server_hawkbit.c. The other thing I'll need to work on is clearly separating and supporting both Gateway Token and Target Token, but I prefer to work on that when the above point is cleared out. Patch incoming. Thank you, Diego Rondini Sr. Embedded Engineer Kynetics www.kynetics.com
diff --git a/corelib/channel_curl.c b/corelib/channel_curl.c index 608f5d3..ec412c0 100644 --- a/corelib/channel_curl.c +++ b/corelib/channel_curl.c @@ -345,6 +345,7 @@ channel_op_res_t channel_set_options(channel_t *this, { channel_curl_t *channel_curl = this->priv; channel_op_res_t result = CHANNEL_OK; + char *token = NULL; if ((curl_easy_setopt(channel_curl->handle, CURLOPT_URL, channel_data->url) != CURLE_OK) || (curl_easy_setopt(channel_curl->handle, CURLOPT_USERAGENT, @@ -397,6 +398,17 @@ channel_op_res_t channel_set_options(channel_t *this, } } + if (channel_data->token != NULL) { + if (asprintf(&token, "Authorization: TargetToken %s", + channel_data->token)) { + if (((channel_curl->header = curl_slist_append( + channel_curl->header, token)) == NULL)) { + result = CHANNEL_EINIT; + goto cleanup; + } + } + } + switch (method) { case CHANNEL_GET: if (curl_easy_setopt(channel_curl->handle, CURLOPT_CUSTOMREQUEST, diff --git a/examples/configuration/swupdate.cfg b/examples/configuration/swupdate.cfg index f9366fd..0d4aba2 100644 --- a/examples/configuration/swupdate.cfg +++ b/examples/configuration/swupdate.cfg @@ -101,6 +101,8 @@ identify : ( # path of the file containing the key for ssl connection # sslcert : string # path of the file containing the certificate for SSL connection +# token : string +# Hawkbit security token # proxy : string # in case the server is reached via a proxy @@ -122,6 +124,7 @@ suricatta : cafile = "/etc/ssl/cafile"; sslkey = "/etc/ssl/sslkey"; sslcert = "/etc/ssl/sslcert"; + token = "3bc13b476cb3962a0c63a5c92beacfh7"; */ }; diff --git a/include/channel_curl.h b/include/channel_curl.h index 98240a9..156d671 100644 --- a/include/channel_curl.h +++ b/include/channel_curl.h @@ -46,6 +46,7 @@ typedef struct { char *cafile; char *sslkey; char *sslcert; + char *token; char *proxy; char *info; unsigned int retry_sleep; diff --git a/suricatta/server_hawkbit.c b/suricatta/server_hawkbit.c index 175396c..ce5374b 100644 --- a/suricatta/server_hawkbit.c +++ b/suricatta/server_hawkbit.c @@ -1527,6 +1527,9 @@ static int suricatta_settings(void *elem, void __attribute__ ((__unused__)) *da GET_FIELD_STRING_RESET(LIBCFG_PARSER, elem, "proxy", tmp); if (strlen(tmp)) SETSTRING(channel_data_defaults.proxy, tmp); + GET_FIELD_STRING_RESET(LIBCFG_PARSER, elem, "token", tmp); + if (strlen(tmp)) + SETSTRING(channel_data_defaults.token, tmp); return 0;