|
1 |
| -# openziti-spring-boot |
2 |
| -Example of hosting a dark service using OpenZiti and Spring Boot |
| 1 | +# Hosting an OpenZiti service using Spring Boot |
| 2 | + |
| 3 | +## What you will need to get started |
| 4 | +* About 30 minutes |
| 5 | +* A favorite text editor or IDE |
| 6 | +* JDK 11 or later |
| 7 | +* Access to a Linux environment with Bash |
| 8 | + * A VM or WSL2 works for Windows users |
| 9 | + |
| 10 | +## Get the code |
| 11 | +The code can be downloaded from [here](https://github.com/netfoundry/openziti-spring-boot/releases) or clone it using Git: |
| 12 | +```shell |
| 13 | + git clone https://github.com/netfoundry/openziti-spring-boot |
| 14 | +``` |
| 15 | + |
| 16 | +Like most Spring guides, you can start from scratch and complete each step, or you can bypass basic |
| 17 | +setup steps that are already familiar to you. Either way, you end up with working code. |
| 18 | + |
| 19 | +# Create the test OpenZiti network |
| 20 | +This example will use a very simple OpenZiti network. |
| 21 | + |
| 22 | +<p align="center"> |
| 23 | +<img id="exampleNetworkImage" src="DemoNetwork.png" alt="Example OpenZiti Network" width="300"/> |
| 24 | +</p> |
| 25 | + |
| 26 | +It isn't important right now to understand all of components of the OpenZiti network. The important things you need to know are: |
| 27 | +1. The controller manages the network. It is responsible for configuration, authentication, and authorization of components that connect to the OpenZiti network. |
| 28 | +2. The router delivers traffic from the client to the server and back again. |
| 29 | + |
| 30 | +Want to know more about OpenZiti? Head over to https://openziti.github.io/ziti/overview.html#overview-of-a-ziti-network. |
| 31 | + |
| 32 | +Let's get into it and create the test network! |
| 33 | + |
| 34 | +## Get the OpenZiti quickstart shell extensions |
| 35 | +OpenZiti provides a set of shell functions that bootstrap the OpenZiti client and testing network. As with any script, it is a good idea to download it first |
| 36 | +and look it over before adding it to your shell. |
| 37 | + |
| 38 | +Leave this terminal open, you'll need it to configure your network too. |
| 39 | + |
| 40 | +```shell |
| 41 | +# Pull the shell extensions |
| 42 | +wget -q https://raw.githubusercontent.com/openziti/ziti/release-next/quickstart/docker/image/ziti-cli-functions.sh |
| 43 | + |
| 44 | +# Source the shell extensions |
| 45 | +. ziti-cli-functions.sh |
| 46 | + |
| 47 | +# Pull the latest Ziti CLI and put it on your shell's classpath |
| 48 | +getLatestZiti yes |
| 49 | +``` |
| 50 | + |
| 51 | +## Start the OpenZiti network |
| 52 | +The shell script you just downloaded includes a few functions to initialize a network. |
| 53 | + |
| 54 | +To start the OpenZiti network overlay, run this in the terminal you used to download the client: |
| 55 | +```shell |
| 56 | + expressInstall |
| 57 | + startZitiController |
| 58 | + waitForController |
| 59 | + startExpressEdgeRouter |
| 60 | +``` |
| 61 | +* **expressInstall**: Creates cryptographic material and configuration files required to run an OpenZiti network. |
| 62 | +* **startZitiController**: Starts the network controller. |
| 63 | +* **startExpressEdgerouter**: Starts the edge router. |
| 64 | + |
| 65 | +## Log into the new network |
| 66 | +The OpenZiti network is now up and running. The next step is to log into the controller and |
| 67 | +establish an administrative session that we will use to configure the example services and identities. |
| 68 | +`ziti-cli-functions` has a function to do this: |
| 69 | +```shell |
| 70 | +zitiLogin |
| 71 | +``` |
| 72 | + |
| 73 | +## Configure the OpenZiti test network |
| 74 | +We'll use a script to configure the OpenZiti network. |
| 75 | + |
| 76 | +The code for the example contains a network directory. |
| 77 | +To configure the network, run this in the same terminal you used to start the OpenZiti network: |
| 78 | +```shell |
| 79 | +./express-network-config.sh |
| 80 | +``` |
| 81 | + |
| 82 | +If the script errors with a lot of `ziti: command not found` then you can re-run this first to put |
| 83 | +ziti back in your terminal path: |
| 84 | +```shell |
| 85 | +getLatestZiti yes |
| 86 | +``` |
| 87 | + |
| 88 | +The script will write out the two identity files (client.json and private-service.json) that you will need for the Java code. |
| 89 | + |
| 90 | +This repository includes a file called [NETWORK-SETUP.md](network/NETWORK-SETUP.md) if you want to learn more about what the script is doing and why. |
| 91 | + |
| 92 | +# Resetting the Ziti demo network |
| 93 | +You're done with this article or things have gone really off the rails, and you want to start over. |
| 94 | + |
| 95 | +There are a couple of commands that need to be run to stop the Ziti network and clean up. |
| 96 | +``` |
| 97 | +stopAllEdgeRouters |
| 98 | +stopZitiController |
| 99 | +unsetZitiEnv |
| 100 | +rm -rf ~/.ziti/quickstart |
| 101 | +``` |
| 102 | + |
| 103 | +# Host service using Spring Boot |
| 104 | +There are three things that need to be done to host an OpenZiti service in a Spring Boot application: |
| 105 | + |
| 106 | +1. Add the OpenZiti spring boot dependency. |
| 107 | +2. Add two properties to the service to configure the service identity and service name. |
| 108 | +3. Add an OpenZiti Tomcat customizer to the main application component scan. |
| 109 | + |
| 110 | +The example code contains an initial/server project. Pull that up in your favorite editor and follow along. |
| 111 | + |
| 112 | +## Add the OpenZiti spring boot dependency |
| 113 | +The openziti spring boot dependency is hosted on maven central. |
| 114 | + |
| 115 | +Add the following to build.gradle: |
| 116 | +```kotlin |
| 117 | +implementation 'org.openziti:ziti-springboot:0.23.12' |
| 118 | +``` |
| 119 | + |
| 120 | +If you prefer maven, add the following to pom.xml: |
| 121 | +```xml |
| 122 | +<dependency> |
| 123 | + <groupId>org.openziti</groupId> |
| 124 | + <artifactId>ziti-springboot</artifactId> |
| 125 | + <version>0.23.12</version> |
| 126 | +</dependency> |
| 127 | +``` |
| 128 | +## Add application properties |
| 129 | +Open the application properties file: `src/main/resources/application.properties` |
| 130 | + |
| 131 | +The Tomcat customizer provided by OpenZiti needs an identity and the name of the service that the identity will bind. If you followed along with the network setup above, then the values will be: |
| 132 | +``` |
| 133 | +ziti.id = ../network/private-service.json |
| 134 | +ziti.serviceName = demo-service |
| 135 | +``` |
| 136 | + |
| 137 | +## Configure the OpenZiti Tomcat customizer |
| 138 | +The Tomcat customizer replaces the standard socket protocol with an OpenZiti protocol that knows |
| 139 | +how Bind a service to accept connections over the Ziti network. To enable this adapter, open up the |
| 140 | +main application class `com.example.restservice.RestServiceApplication`. |
| 141 | + |
| 142 | +Replace |
| 143 | +``` |
| 144 | +@SpringBootApplication |
| 145 | +``` |
| 146 | + |
| 147 | +With |
| 148 | +``` |
| 149 | +@SpringBootApplication (scanBasePackageClasses = {ZitiTomcatCustomizer.class, GreetingController.class}) |
| 150 | +``` |
| 151 | + |
| 152 | +## Run the application |
| 153 | +That’s all you need to do! The OpenZiti Java SDK will connect to the test network, authenticate, |
| 154 | +and bind your service so that OpenZiti overlay network clients can connect to it. |
| 155 | + |
| 156 | +To run the application, enter the following in a terminal window (in your project directory) |
| 157 | +```shell |
| 158 | +./gradlew bootRun |
| 159 | +``` |
| 160 | + |
| 161 | +If you use maven, run the following in a terminal window in your project directory: |
| 162 | +```shell |
| 163 | +./mvnw spring-boot:run |
| 164 | +``` |
| 165 | + |
| 166 | +# Test your new Spring Boot service |
| 167 | +The Spring Boot service you have just created is now totally dark. It has no listening ports at all. |
| 168 | +Go ahead - run `netcat` and find out for yourself! |
| 169 | + |
| 170 | +```shell |
| 171 | +netstat -anp | grep 8080 |
| 172 | +``` |
| 173 | +You should find nothing `LISTENING`. Now, the only way to access it is via the OpenZiti network! |
| 174 | +Let’s write a simple client to connect to it and check that everything is working correctly. |
| 175 | + |
| 176 | +This section will use the OpenZiti Java SDK to connect to the OpenZiti network. The example source |
| 177 | +includes a project and a class that takes care of the boilerplate stuff for you. |
| 178 | + |
| 179 | +If you want to skip building the client then you can skip ahead to [Run The Client](#run-the-client). |
| 180 | + |
| 181 | +## Connect to OpenZiti |
| 182 | +The Java SDK needs to be initialized with an OpenZiti identity. It is polite to destroy the context |
| 183 | +once the code is done, so we’ll wrap it up in a `try/catch` with a `finally` block. |
| 184 | + |
| 185 | +```java |
| 186 | +ZitiContext zitiContext = null; |
| 187 | +try { |
| 188 | + zitiContext = Ziti.newContext(identityFile, "".toCharArray()); |
| 189 | + |
| 190 | + if (null == zitiContext.getService(serviceName,10000)) { |
| 191 | + throw new IllegalArgumentException(String.format("Service %s is not available on the OpenZiti network",serviceName)); |
| 192 | + } |
| 193 | + |
| 194 | +} catch (Throwable t) { |
| 195 | + log.error("OpenZiti network test failed", t); |
| 196 | +} |
| 197 | +finally { |
| 198 | + if( null != zitiContext ) zitiContext.destroy(); |
| 199 | +} |
| 200 | +``` |
| 201 | +* **Ziti.newContext:** Loads the OpenZiti identity and starts the connection process. |
| 202 | +* **zitiContext.getService** It can take a little while to establish the connection with the OpenZiti |
| 203 | +network fabric. For long-running applications this is typically not a problem, but for this little |
| 204 | +client we need to give the network some time to get everything ready. |
| 205 | +* **zitiContext.destroy():** Disposes of the context and cleans up resources locally and on the |
| 206 | +OpenZiti network. |
| 207 | + |
| 208 | +## Send a request to the service |
| 209 | +The client has a connection to the test OpenZiti network. Now the client can ask OpenZiti to dial the service and send some data. |
| 210 | + |
| 211 | +This client is for demonstration purposes only. You would never write a raw HTTP request like this |
| 212 | +in a real app. OpenZiti has a couple of examples using OKHttp and Netty if you want to work this |
| 213 | +code up using a real HTTP client. The examples can be found at |
| 214 | +https://github.com/openziti/ziti-sdk-jvm/tree/main/samples. |
| 215 | + |
| 216 | +```java |
| 217 | +log.info("Dialing service"); |
| 218 | +ZitiConnection conn = zitiContext.dial(serviceName); |
| 219 | +String request = "GET /greeting?name=MyName HTTP/1.1\n" + |
| 220 | +"Accept: */*\n" + |
| 221 | +"Host: example.web\n" + |
| 222 | +"\n"; |
| 223 | +log.info("Sending request"); |
| 224 | +conn.write(request.getBytes(StandardCharsets.UTF_8)); |
| 225 | +``` |
| 226 | +* **ZitiConnection:** A socket connection over the OpenZiti network fabric that can be used to exchange data with a Ziti service. |
| 227 | +* **zitiContext.dial:** Dialing a service opens a connection through the OpenZiti to the service. |
| 228 | +* **request:** The connection is essentially a plain socket. The contents of the request string is a plain HTTP GET to the greeting endpoint in the Spring Boot app. |
| 229 | +* **con.write:** Sends the request over the OpenZiti network. |
| 230 | + |
| 231 | +## Read the service response |
| 232 | +The service will respond to the request with a json greeting. Read the greeting and write it to the log. |
| 233 | +```java |
| 234 | +byte[] buff = new byte[1024]; |
| 235 | +int i; |
| 236 | +log.info("Reading response"); |
| 237 | +while (0 < (i = conn.read(buff,0, buff.length))) { |
| 238 | + log.info("=== " + new String(buff, 0, i) ); |
| 239 | +} |
| 240 | +``` |
| 241 | +* **con.read:** Read data sent back from the Spring Boot service via the OpenZiti connection. |
| 242 | + |
| 243 | +## Run The Client |
| 244 | +To run the client, run the following in a terminal window (in the client project): |
| 245 | +```shell |
| 246 | +./gradlew build run |
| 247 | +``` |
| 248 | + |
| 249 | +If you use maven, run the following in a terminal window (in the client project): |
| 250 | +```shell |
| 251 | +./mvnw package exec:java |
| 252 | +``` |
| 253 | +# Dig Deeper |
| 254 | +* **OpenZiti documentation:** https://openziti.github.io/ziti/overview.html |
| 255 | +* **OpenZiti Github project:** https://github.com/openziti |
| 256 | +* **Spring Boot Rest Sample:** https://spring.io/guides/gs/rest-service/ |
| 257 | +* **NetFoundry hosted OpenZiti NaaS offering:** https://netfoundry.io |
| 258 | + |
| 259 | +**Spring and Spring Boot are trademarks of Pivotal Software, Inc. in the U.S. and other countries.* |
0 commit comments