Coder Social home page Coder Social logo

nordicsemiconductor / nat-testserver Goto Github PK

View Code? Open in Web Editor NEW
3.0 3.0 0.0 2.81 MB

Receives NAT test messages from the NAT-TestFirmware and logs them and timeout occurances to S3

License: BSD 3-Clause "New" or "Revised" License

Go 46.43% TypeScript 52.11% JavaScript 0.25% Dockerfile 1.20%
nat-test

nat-testserver's Introduction

NAT-TestServer

Test Docker semantic-release Commitizen friendly code style: golint code style: prettier ESLint: TypeScript

Receives NAT test messages from the NAT Test Firmware and logs them and timeout occurances to S3.

NAT Timeout determination

The server listens for TCP and UPD connections. Clients can send messages according to the schema.json which contain an interval property. This instructs the server to wait that amount in seconds before returning a response.

On TCP connections the connection is considered to be timed out when the server cannot reply to the client after the interval has passed. The TCP connection will ensure that the client receives the message from the server if it is still connected.

On UDP connections the connection is considered to be timed out when the server does not receive a new message from the client within 60 seconds after having sent the response for the previous message. There is no other way to ensure that the connection is intact.

The results are logged to an S3 bucket (on entry per test), and a lambda will collect these log files and combine them into larger files (per hour, day, and month) to improve querying performance with Athena.

During this process the log entries are enriched with SIM vendor information: the ICCID is matched against a list of known vendors. If an unknown vendor is encountered, the log files will not show up in the report. In this case the list needs to be updated. Once this is done, log files can be manually enriched using:

node -e 'const { enrich } = require("./dist/enrichSimVendor/cli.js"); enrich();'

Testing

Make these environment variable available:

ℹ️ Linux users can use direnv to simplify the process.

export AWS_REGION=<...>
export AWS_BUCKET=<...>
export AWS_ACCESS_KEY_ID=<...>
export AWS_SECRET_ACCESS_KEY=<...>

To test if the server is listening on local ports and saves the correct data, execute the command

go get -v -t -d ./...
go test -v

Running in Docker

docker build -t nordicsemiconductor/nat-testserver .
docker run \
    -e AWS_BUCKET \
    -e AWS_REGION \
    -e AWS_ACCESS_KEY_ID \
    -e AWS_SECRET_ACCESS_KEY \
    --rm --net=host -P nordicsemiconductor/nat-testserver:latest

# Send a package
echo '{"op": "310410", "ip": ["10.160.1.82"], "cell_id": 84486415, "ue_mode": 2, "iccid": "8931080019073497795F", "interval":1}' | nc -w1 -u 127.0.0.1 3050

Deploy to AWS

Note on the public IP

Currently there is no Load Balancer in front of the server (after all this service was developed to test NAT timeouts on UDP and TCP connections, so we need to have the actual servers instance terminate the connection, not the load balancer).

Therefore the public IP needs to be manually updated in the DNS record used by the firmware because there is no other way right now when using Fargate.

For this there is a lambda function which listens to change events from the cluster in the service account and updates a DNS record on a Route53 Hosted Zone which is in a separate DNS account. Unfortunately it is currently not possible to limit access to subdomains in a Hosted Zone.

Prepare a role in the domain account with these permissions, replace

  • <Zone ID> with the ID of your hosted zone.
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "VisualEditor0",
      "Effect": "Allow",
      "Action": "route53:ChangeResourceRecordSets",
      "Resource": "arn:aws:route53:::hostedzone/<Zone ID>"
    }
  ]
}

Create a trust relationship for the service account, replace

  • <Account ID of the service account> with the account id of the service account
  • <External ID> with a random string (e.g. use a UUIDv4)
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::<Account ID of the service account>:root"
      },
      "Action": "sts:AssumeRole",
      "Condition": {
        "StringEquals": {
          "sts:ExternalId": "<External ID>"
        }
      }
    }
  ]
}

Deploy

Make these environment variable available:

ℹ️ Linux users can use direnv to simplify the process.

export AWS_REGION=<...>
export AWS_ACCESS_KEY_ID=<Access Key ID of the service account>
export AWS_SECRET_ACCESS_KEY=<Secret Access Key of the service account>
export STS_ROLE_ARN=<ARN of the role created in the domain account>
export STS_EXTERNAL_ID=<External ID from above>
export HOSTED_ZONE_ID=<Zone ID from above>
export RECORD_NAME=<FQDN of the A record to update>

Install dependencies

npm ci

Set the ID of the stack

export STACK_NAME="${STACK_NAME:-nat-test-resources}"

Prepare the account for CDK resources:

npx cdk -a 'node dist/cdk-sourcecode.js' deploy
npx cdk bootstrap

Deploy the ECR stack to an AWS Account

npx cdk -a 'node dist/cdk-ecr.js' deploy ${STACK_NAME}-ecr

Publish the docker image to AWS Elastic Container Registry

ECR_REPOSITORY_NAME=`aws cloudformation describe-stacks --stack-name ${STACK_NAME}-ecr | jq -r '.Stacks[0].Outputs[] | select(.OutputKey == "cdEcrRepositoryName") | .OutputValue'`
ECR_REPOSITORY_URI=`aws cloudformation describe-stacks --stack-name ${STACK_NAME}-ecr | jq -r '.Stacks[0].Outputs[] | select(.OutputKey == "cdEcrRepositoryUri") | .OutputValue'`
aws ecr get-login-password --region ${AWS_REGION} | docker login --username AWS --password-stdin ${ECR_REPOSITORY_URI}
docker tag nordicsemiconductor/nat-testserver:latest ${ECR_REPOSITORY_URI}:latest
docker push ${ECR_REPOSITORY_URI}:latest

Deploy the server stack to an AWS account

npx cdk deploy $STACK_NAME

Continuous Deployment

Continuous Deployment of releases is done through GitHub Actions. Configure these secrets:

  • CD_AWS_REGION: Region where the stack is deployed
  • CD_AWS_ACCESS_KEY_ID: Access key ID for the CD user
  • CD_AWS_SECRET_ACCESS_KEY: Secret access key for the CD user
  • STS_ROLE_ARN: ARN of the role created in the domain account
  • STS_EXTERNAL_ID: External ID from above
  • HOSTED_ZONE_ID: Zone ID from above
  • RECORD_NAME: FQDN of the A record to update
  • USER_GITHUB_TOKEN_FOR_ACTION_TRIGGER: In order to be able to trigger this action, a GitHub user token with the permissions public_repo, repo:status, repo_deployment is needed (the default Actions credentials can't trigger other Actions).

Afterwards the Test Action will trigger a deployment.

Deploying a new version of the server manually

Publish a new version of the image to ECR (see above), then trigger a new deployment:

SERVICE_ID=`aws cloudformation describe-stacks --stack-name ${STACK_NAME} | jq -r '.Stacks[0].Outputs[] | select(.OutputKey == "fargateServiceArn") | .OutputValue'`
CLUSTER_NAME=`aws cloudformation describe-stacks --stack-name ${STACK_NAME} | jq -r '.Stacks[0].Outputs[] | select(.OutputKey == "clusterArn") | .OutputValue'`
aws ecs update-service --service $SERVICE_ID --cluster $CLUSTER_NAME --force-new-deployment

nat-testserver's People

Contributors

boundlesscalm avatar coderbyheart avatar dependabot[bot] avatar snyk-bot avatar

Stargazers

 avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

nat-testserver's Issues

Support multiple IPs

Devices can have multiple IPs:

{
        "op":   "24201",
        "ip":   "10.127.183.249 2A02:2121:008D:1793:0000:004B:0E9E:7901",
        "cell_id":      20504576,
        "ue_mode":      2,
        "iccid":        "89470060171107305562",
        "interval":     1
}

Run the server anywhere

Hi,

This is an interesting project. It is necessary to know and understand NAT timeouts and the values configured by each carrier when developing cellular-IoT applications in order to configure the keepalive value correctly and save power on the client-side (embedded device running on battery).

Example use-case: for quick tests in the field, I can run the client and connect to a know IPv4 or IPv6 address. I do not necessarily need DNS. I get logs from the client application and I should be able to find the NAT timeout value without logs from the server.

I see that the server is tightly coupled to a cloud provider, AWS in this case. Would it be possible to provide a version of the server application (as a native Go application or as a Docker image) that I could run anywhere (on any Linux host or Docker platform)? Server-side logs could simply be printed to stdout or to a file.

I am asking for a more simple version compared to what you already provide.

Nother question: the imei (part of the scheme) can be used to identify each client. Does the server support multiple connections (I did not look at the server implementation yet)?

Data is stored with empty ip members

{
  "Received": "2020-04-23T12:51:08.19+0000",
  "Protocol": "UDP",
  "IP": "62.93.143.64:50881",
  "Timeout": true,
  "Data": {
    "op": "24201",
    "ip": ["10.160.233.45", "", "", "", "", "", "", "", "", ""],
    "cell_id": 21229824,
    "ue_mode": 2,
    "iccid": "8931089318104314834F",
    "interval": 100
  }
}

Put server instance configuration in code

JSON error cases tests are not working

See f38f485

I have added a line from the good cases to the error cases, and the tests still pass.

This means the error case tests are actually not testing that the parser detects malformed input.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.