hawkbit: adding authentication using security token

Message ID 1512508319-30950-1-git-send-email-ayoub.zaki@embexus.com
State Changes Requested
Headers show
Series
  • hawkbit: adding authentication using security token
Related show

Commit Message

Ayoub Zaki Dec. 5, 2017, 9:11 p.m.
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(+)

Comments

Diego Rondini Dec. 6, 2017, 9:50 a.m. | #1
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
Ayoub Zaki Dec. 6, 2017, 1:02 p.m. | #2
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,
Diego Rondini Dec. 6, 2017, 1:35 p.m. | #3
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
Stefano Babic Dec. 6, 2017, 1:57 p.m. | #4
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
Stefano Babic Dec. 6, 2017, 2:06 p.m. | #5
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
Ayoub Zaki Dec. 6, 2017, 2:30 p.m. | #6
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,

Patch

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;