├── LICENSE ├── Makefile ├── README.md ├── cmd ├── createdb │ └── createSqlite3Database.go ├── freetaxii │ ├── etc │ │ ├── README.md │ │ ├── freetaxii.conf │ │ └── tls │ │ │ └── README.md │ ├── freetaxii.go │ └── templates │ │ ├── README.md │ │ └── html │ │ ├── apirootResource.html │ │ ├── collectionResource.html │ │ ├── collectionsResource.html │ │ ├── discoveryResource.html │ │ ├── manifestResource.html │ │ ├── objectsResource.html │ │ └── versionsResource.html └── verifyconfig │ └── verifyconfig.go └── internal ├── config ├── jsonTypes.go ├── jsonTypes_test.go ├── serverConfig.go ├── verify.go ├── verifyGlobalConfig.go ├── verifyHTMLConfig.go └── verifyServicesConfig.go ├── handlers ├── auth.go ├── contentHandler.go ├── doc.go ├── errors.go ├── getObject_test.go ├── server.go └── taxiiHandler.go └── headers ├── doc.go └── headers.go /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Copyright 2015-2018 Bret Jordan, All rights reserved. 2 | # 3 | # Use of this source code is governed by an Apache 2.0 license 4 | # that can be found in the LICENSE file in the root of the source tree. 5 | 6 | GO_CMD=go 7 | GO_BUILD=$(GO_CMD) build 8 | GO_CLEAN=$(GO_CMD) clean 9 | GO_GET=$(GO_CMD) get 10 | GO_INSTALL=$(GO_CMD) install -v 11 | NO_COLOR=\033[0m 12 | OK_COLOR=\033[32;01m 13 | ERROR_COLOR=\033[31;01m 14 | WARN_COLOR=\033[33;01m 15 | 16 | 17 | # Binary filename 18 | BINARY=freetaxii 19 | VERSION=0.3.1 20 | BUILD_DIR = srcbuild 21 | BIN_DIR = bin 22 | LOG_DIR = log 23 | DB_DIR = db 24 | ETC_DIR = etc 25 | TEMPLATES_DIR = templates 26 | 27 | # The build version that we want to pass in to the application during compile time 28 | BUILD=`git rev-parse HEAD` 29 | 30 | # Setup the -ldflags option for go build here, interpolate the variable values 31 | # LDFLAGS=-ldflags "-X main.Build=$(BUILD)" 32 | 33 | 34 | .PHONY: clean distro 35 | 36 | # Default target builds FreeTAXII 37 | default: 38 | @echo "$(OK_COLOR)==> Please run \"make distro\"...$(NO_COLOR)"; 39 | 40 | # Installs FreeTAXII and copies needed files 41 | distro: 42 | @echo "$(OK_COLOR)==> Removing Existing Distribution Package...$(NO_COLOR)"; \ 43 | if [ -d $(BUILD_DIR) ] ; then rm -rf $(BUILD_DIR) ; fi 44 | 45 | @echo "$(OK_COLOR)==> Setting Up Distribution Directories...$(NO_COLOR)"; \ 46 | mkdir -p $(BUILD_DIR)/$(BINARY)-$(VERSION)/$(BIN_DIR); \ 47 | mkdir -p $(BUILD_DIR)/$(BINARY)-$(VERSION)/$(DB_DIR); \ 48 | mkdir -p $(BUILD_DIR)/$(BINARY)-$(VERSION)/$(ETC_DIR)/tls; \ 49 | mkdir -p $(BUILD_DIR)/$(BINARY)-$(VERSION)/$(LOG_DIR); \ 50 | mkdir -p $(BUILD_DIR)/$(BINARY)-$(VERSION)/$(TEMPLATES_DIR); 51 | 52 | @echo "$(OK_COLOR)==> Building Application Files...$(NO_COLOR)"; \ 53 | $(GO_BUILD) -v -o $(BUILD_DIR)/$(BINARY)-$(VERSION)/$(BINARY) cmd/freetaxii/freetaxii.go; \ 54 | $(GO_BUILD) -v -o $(BUILD_DIR)/$(BINARY)-$(VERSION)/$(BIN_DIR)/createSqlite3Database cmd/createdb/createSqlite3Database.go; \ 55 | $(GO_BUILD) -v -o $(BUILD_DIR)/$(BINARY)-$(VERSION)/$(BIN_DIR)/verifyconfig cmd/verifyconfig/verifyconfig.go; 56 | 57 | @echo "$(OK_COLOR)==> Copying Needed Files...$(NO_COLOR)"; \ 58 | cp -R cmd/freetaxii/templates/* $(BUILD_DIR)/$(BINARY)-$(VERSION)/$(TEMPLATES_DIR)/; \ 59 | cp cmd/freetaxii/etc/freetaxii.conf $(BUILD_DIR)/$(BINARY)-$(VERSION)/$(ETC_DIR)/; \ 60 | touch $(BUILD_DIR)/$(BINARY)-$(VERSION)/$(LOG_DIR)/$(BINARY).log; 61 | 62 | @echo "$(OK_COLOR)==> Creating Database File...$(NO_COLOR)"; \ 63 | cd $(BUILD_DIR)/$(BINARY)-$(VERSION)/$(BIN_DIR)/; \ 64 | ./createSqlite3Database; 65 | 66 | @echo "$(OK_COLOR)==> Move Database File To DB Directory...$(NO_COLOR)"; \ 67 | mv $(BUILD_DIR)/$(BINARY)-$(VERSION)/$(BIN_DIR)/freetaxii.db $(BUILD_DIR)/$(BINARY)-$(VERSION)/$(DB_DIR); 68 | 69 | @echo "$(OK_COLOR)==> Creating Tarball...$(NO_COLOR)"; \ 70 | cd $(BUILD_DIR)/; \ 71 | tar -cf $(BINARY)-$(VERSION).tar $(BINARY)-$(VERSION); 72 | 73 | @echo "$(OK_COLOR)==> Compressing Tarball...$(NO_COLOR)"; \ 74 | cd $(BUILD_DIR)/; \ 75 | gzip $(BINARY)-$(VERSION).tar; 76 | 77 | @echo "$(OK_COLOR)==> Moving Compressed Tarball...$(NO_COLOR)"; \ 78 | mv $(BUILD_DIR)/$(BINARY)-$(VERSION).tar.gz ./ 79 | 80 | 81 | # Clean up the project: delete binaries 82 | clean: 83 | @echo "$(OK_COLOR)==> Removing Existing Distribution Package $(BINARY)...$(NO_COLOR)"; \ 84 | if [ -d $(BUILD_DIR) ] ; then rm -rf $(BUILD_DIR) ; fi 85 | 86 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FreeTAXII/server # 2 | 3 | [![Go Report Card](https://goreportcard.com/badge/github.com/freetaxii/server)](https://goreportcard.com/report/github.com/freetaxii/server) [![GoDoc](https://godoc.org/github.com/freetaxii/server?status.png)](https://godoc.org/github.com/freetaxii/server) 4 | 5 | The FreeTAXII Server is a TAXII 2 Server written in Go (golang) 6 | 7 | ## Version ## 8 | 0.3.1 9 | 10 | 11 | ## Installation ## 12 | 13 | This package can be installed with the go get command: 14 | 15 | ``` 16 | go get -u -v github.com/freetaxii/server/cmd/freetaxii 17 | cd github.com/freetaxii/server/cmd/freetaxii 18 | go build freetaxii.go 19 | ``` 20 | 21 | To create the Sqlite3 database, run the following command: 22 | 23 | ``` 24 | cd github.com/freetaxii/server/cmd/createdb 25 | go run createSqlite3Database.go 26 | ``` 27 | 28 | ## Dependencies ## 29 | 30 | This software uses the following external libraries: 31 | ``` 32 | getopt 33 | go get github.com/pborman/getopt 34 | Copyright (c) 2017 Google Inc. All rights reserved. 35 | 36 | gorilla/mux 37 | go get github.com/gorilla/mux 38 | Copyright (c) 2012 Rodrigo Moraes 39 | 40 | gologme/log 41 | go get github.com/gologme/log 42 | Copyright (c) 2017 Bret Jordan. All rights reserved. 43 | 44 | libstix2 45 | go get github.com/freetaxii/libstix2 46 | Copyright (c) 2015-2018 Bret Jordan. All rights reserved. 47 | 48 | ``` 49 | 50 | This software uses the following builtin libraries: 51 | ``` 52 | crypto/tls, database/sql, encoding/json, errors, fmt, html/template, io/ioutil, log, net/http, os, path, strconv, strings, time 53 | Copyright 2009 The Go Authors 54 | ``` 55 | 56 | ## Features ## 57 | 58 | Below is a list of major features and which ones have been implemented: 59 | 60 | - [x] TLS 1.2 61 | - [x] Discovery Service 62 | - [x] Multiple Discovery Services 63 | - [x] API Root Service 64 | - [x] Multiple API Roots Services 65 | - [x] Endpoints 66 | - [x] Discovery 67 | - [x] API Root 68 | - [x] Collections 69 | - [x] Collection 70 | - [x] Objects (GET) 71 | - [x] Objects (POST) 72 | - [x] Objects By ID 73 | - [x] Object Versions 74 | - [x] Manifest 75 | - [ ] Status 76 | - [x] URL Filtering 77 | - [x] added_after 78 | - [x] limit 79 | - [x] match[id] 80 | - [x] match[type] 81 | - [x] match[version] 82 | - [x] match[spec_version] 83 | - [x] Configuration 84 | - [x] From a file 85 | - [ ] From a database 86 | - [x] Pagination 87 | - [ ] Authentication 88 | - [ ] Max Content Size Checking 89 | - [x] HTML Templates 90 | - [x] Per Service Templates 91 | 92 | 93 | ## License ## 94 | 95 | This is free software, licensed under the Apache License, Version 2.0. 96 | 97 | 98 | ## Copyright ## 99 | 100 | Copyright 2015-2018 Bret Jordan, All rights reserved. 101 | -------------------------------------------------------------------------------- /cmd/createdb/createSqlite3Database.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015-2018 Bret Jordan, All rights reserved. 2 | // 3 | // Use of this source code is governed by an Apache 2.0 license 4 | // that can be found in the LICENSE file in the root of the source tree. 5 | 6 | package main 7 | 8 | import ( 9 | "database/sql" 10 | "fmt" 11 | "os" 12 | 13 | "github.com/freetaxii/libstix2/datastore/sqlite3" 14 | "github.com/gologme/log" 15 | "github.com/pborman/getopt" 16 | ) 17 | 18 | // These global variables hold build information. The Build variable will be 19 | // populated by the Makefile and uses the Git Head hash as its identifier. 20 | // These variables are used in the console output for --version and --help. 21 | var ( 22 | Version = "0.3.2" 23 | Build string 24 | ) 25 | 26 | // These global variables are for dealing with command line options 27 | var ( 28 | defaultDatabaseFilename = "freetaxii.db" 29 | sOptDatabaseFilename = getopt.StringLong("filename", 'f', defaultDatabaseFilename, "Database Filename", "string") 30 | bOptHelp = getopt.BoolLong("help", 0, "Help") 31 | bOptVer = getopt.BoolLong("version", 0, "Version") 32 | ) 33 | 34 | func main() { 35 | processCommandLineFlags() 36 | 37 | // We are not using the sqlite New() function as it looks for tables that do 38 | // not yet exist 39 | var ds sqlite3.Store 40 | ds.Filename = *sOptDatabaseFilename 41 | 42 | db, sqlerr := sql.Open("sqlite3", ds.Filename) 43 | defer db.Close() 44 | if sqlerr != nil { 45 | log.Fatalln("Unable to open file %s due to error: %v", ds.Filename, sqlerr) 46 | } 47 | ds.DB = db 48 | 49 | ds.CreateSTIXTables() 50 | ds.CreateVocabTables() 51 | ds.PopulateVocabTables() 52 | ds.CreateTAXIITables() 53 | 54 | } 55 | 56 | // -------------------------------------------------- 57 | // Private functions 58 | // -------------------------------------------------- 59 | 60 | // processCommandLineFlags - This function will process the command line flags 61 | // and will print the version or help information as needed. 62 | func processCommandLineFlags() { 63 | getopt.HelpColumn = 35 64 | getopt.DisplayWidth = 120 65 | getopt.SetParameters("") 66 | getopt.Parse() 67 | 68 | // Lets check to see if the version command line flag was given. If it is 69 | // lets print out the version infomration and exit. 70 | if *bOptVer { 71 | printOutputHeader() 72 | os.Exit(0) 73 | } 74 | 75 | // Lets check to see if the help command line flag was given. If it is lets 76 | // print out the help information and exit. 77 | if *bOptHelp { 78 | printOutputHeader() 79 | getopt.Usage() 80 | os.Exit(0) 81 | } 82 | } 83 | 84 | // printOutputHeader - This function will print a header for all console output 85 | func printOutputHeader() { 86 | fmt.Println("") 87 | fmt.Println("FreeTAXII - STIX Table Creator") 88 | fmt.Println("Copyright: Bret Jordan") 89 | fmt.Println("Version:", Version) 90 | if Build != "" { 91 | fmt.Println("Build:", Build) 92 | } 93 | fmt.Println("") 94 | } 95 | -------------------------------------------------------------------------------- /cmd/freetaxii/etc/README.md: -------------------------------------------------------------------------------- 1 | # FreeTAXII/freetaxii-server/etc/freetaxii.conf # 2 | 3 | The FreeTAXII-Server is a TAXII 2 Server written in Go (golang) 4 | 5 | 6 | ## Configuration File Directives ## 7 | 8 | The freetaxii.conf configuration file uses a JSON encoded configuration file with the following directives: 9 | 10 | 11 | ### Global Directives ### 12 | - system 13 | - logging 14 | - discoveryservice 15 | - apirootservice 16 | - discoveryresources 17 | - apirootresources 18 | - collectionresources 19 | 20 | ### system directives ### 21 | 22 | #### protocol #### 23 | The protocol being used for this server. The only two options are http and https 24 | 25 | #### listen #### 26 | The IP address and port number that this server listens on. Example 127.0.0.1:8080 27 | 28 | #### prefix #### 29 | The installation prefix for the server. Example /opt/freetaxii 30 | 31 | #### dbconfig #### 32 | A boolean flag to tell the server if the server configuration comes from this text file or a database. 33 | 34 | #### dbtype #### 35 | The type of database that contains the server configuration information. Currently the only option is sqlite3 36 | 37 | #### dbfile #### 38 | The location of the database file that contains the server configuration 39 | 40 | #### htmldir #### 41 | The html template directory 42 | 43 | #### tlskey #### 44 | The name of the TLS private key that is located in etc/tls/ 45 | 46 | #### tlscrt #### 47 | The name of the TLS public certificate that is located in etc/tls/ 48 | 49 | ### logging directives ### 50 | 51 | #### enabled #### 52 | A boolean flag to enable logging 53 | 54 | #### loglevel #### 55 | An integer with a value of 1-5 that specifies the current logging level. The log levels are currently defined as: 56 | 57 | - Log Level 1 = Information messages 58 | - Log Level 3 = Information and warning messages 59 | - Log Level 5 = Information, warning, and debug messages 60 | 61 | #### logfile #### 62 | The location of the log file. Example: log/freetaxii.log 63 | 64 | ## License ## 65 | 66 | This is free software, licensed under the Apache License, Version 2.0. 67 | 68 | 69 | ## Copyright ## 70 | 71 | Copyright 2015-2018 Bret Jordan, All rights reserved. 72 | -------------------------------------------------------------------------------- /cmd/freetaxii/etc/freetaxii.conf: -------------------------------------------------------------------------------- 1 | { 2 | "global" : { 3 | "prefix" : "/opt/go/src/github.com/freetaxii/server/cmd/freetaxii/", 4 | "listen" : "127.0.0.1:8000", 5 | "protocol" : "https", 6 | "tlsdir" : "etc/tls/", 7 | "tlskey" : "server.key", 8 | "tlscrt" : "server.crt", 9 | "dbconfig" : false, 10 | "dbtype" : "sqlite3", 11 | "dbfile" : "db/freetaxii.db", 12 | "serverrecordlimit" : 10 13 | }, 14 | "html" : { 15 | "enabled" : true, 16 | "templatedir" : "templates/html/", 17 | "templatefiles" : { 18 | "discovery" : "discoveryResource.html", 19 | "apiroot" : "apirootResource.html", 20 | "collections" : "collectionsResource.html", 21 | "collection" : "collectionResource.html", 22 | "objects" : "objectsResource.html", 23 | "versions" : "versionsResource.html", 24 | "manifest" : "manifestResource.html" 25 | } 26 | }, 27 | "logging" : { 28 | "enabled" : true, 29 | "level" : 3, 30 | "logfile" : "log/freetaxii.log" 31 | }, 32 | "discovery_server" : { 33 | "enabled" : true, 34 | "services" : [ 35 | { 36 | "enabled" : true, 37 | "path" : "/taxii2/", 38 | "resourceid" : "discovery--1" 39 | } 40 | ] 41 | }, 42 | "apiroot_server" : { 43 | "enabled" : true, 44 | "services" : [ 45 | { 46 | "enabled" : true, 47 | "path" : "/api1/", 48 | "resourceid" : "apiroot--1", 49 | "collections" : { 50 | "enabled" : true, 51 | "readaccess" : [ 52 | "collection--1", 53 | "collection--3" 54 | ], 55 | "writeaccess" : [ 56 | "collection--2", 57 | "collection--3" 58 | ] 59 | } 60 | } 61 | ] 62 | }, 63 | "discovery_resources" : { 64 | "discovery--1" : { 65 | "title" : "FreeTAXII Discovery Service", 66 | "description" : "A listing of API-Roots that this server knows about", 67 | "contact" : "FreeTAXII@gmail.com", 68 | "default" : "https://127.0.0.1:8000/api1/", 69 | "api_roots" : [ 70 | "https://127.0.0.1:8000/api1/", 71 | "https://127.0.0.1:8000/api2/" 72 | ] 73 | } 74 | }, 75 | "apiroot_resources" : { 76 | "apiroot--1" : { 77 | "title" : "API Test 1", 78 | "description" : "FreeTAXII API 1 Test Address", 79 | "versions" : [ 80 | "application/taxii+json;version=2.1" 81 | ], 82 | "max_content_length" : 10485760 83 | } 84 | }, 85 | "collection_resources" : { 86 | "collection--1" : { 87 | "id" : "22f763c1-e478-4765-8635-e4c32db665ea", 88 | "title" : "Read-Only TestLab Collection", 89 | "description" : "This is a Read-Only collection for use with the FreeTAXII TestLab tool", 90 | "can_read" : true, 91 | "can_write" : false, 92 | "media_types" : [ 93 | "application/stix+json;version=2.1" 94 | ] 95 | }, 96 | "collection--2" : { 97 | "id" : "4f7327e2-f5b4-4269-b6e0-3564d174ce69", 98 | "title" : "Write-Only TestLab Collection", 99 | "description" : "This is a Write-Only collection for use with the FreeTAXII TestLab tool", 100 | "can_read" : false, 101 | "can_write" : true, 102 | "media_types" : [ 103 | "application/stix+json;version=2.1" 104 | ] 105 | }, 106 | "collection--3" : { 107 | "id" : "8c49f14d-8ea3-4f03-ab28-19dbca973dde", 108 | "title" : "Read-Write TestLab Collection", 109 | "description" : "This is a Read-Write collection for use with the FreeTAXII TestLab tool", 110 | "can_read" : true, 111 | "can_write" : true, 112 | "media_types" : [ 113 | "application/stix+json;version=2.1" 114 | ] 115 | } 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /cmd/freetaxii/etc/tls/README.md: -------------------------------------------------------------------------------- 1 | # FreeTAXII/freetaxii-server/etc/tls # 2 | 3 | The FreeTAXII-Server is a TAXII 2 Server written in Go (golang) 4 | 5 | 6 | ## Installation ## 7 | 8 | To generate an RSA key and self-signed certificate for your server you can do the following in the freetaxii/etc/tls directory: 9 | 10 | ``` 11 | openssl req -x509 -nodes -newkey rsa:4096 -keyout server.rsa.key -out server.rsa.crt -days 3650 12 | chmod 600 server.rsa.key 13 | ln -sf server.rsa.key server.key 14 | ln -sf server.rsa.crt server.crt 15 | 16 | ``` 17 | 18 | ## Configuraiton ## 19 | 20 | The following header was added to each of the handlers. This was done per RFC 6797 (https://tools.ietf.org/html/rfc6797) 21 | Additional information can be found here: (https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security) 22 | 23 | ``` 24 | w.Header().Add("Strict-Transport-Security", "max-age=86400; includeSubDomains") 25 | ``` 26 | 27 | 28 | ## License ## 29 | 30 | This is free software, licensed under the Apache License, Version 2.0. 31 | 32 | 33 | ## Copyright ## 34 | 35 | Copyright 2015-2018 Bret Jordan, All rights reserved. 36 | -------------------------------------------------------------------------------- /cmd/freetaxii/freetaxii.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015-2018 Bret Jordan, All rights reserved. 2 | // 3 | // Use of this source code is governed by an Apache 2.0 license 4 | // that can be found in the LICENSE file in the root of the source tree. 5 | 6 | package main 7 | 8 | import ( 9 | "crypto/tls" 10 | "fmt" 11 | "net/http" 12 | "os" 13 | 14 | "github.com/freetaxii/libstix2/datastore" 15 | "github.com/freetaxii/libstix2/datastore/sqlite3" 16 | "github.com/freetaxii/libstix2/resources/collections" 17 | "github.com/freetaxii/server/internal/config" 18 | "github.com/freetaxii/server/internal/handlers" 19 | "github.com/gologme/log" 20 | "github.com/gorilla/mux" 21 | "github.com/pborman/getopt" 22 | ) 23 | 24 | /* 25 | These global variables hold build information. The Build variable will be 26 | populated by the Makefile and uses the Git Head hash as its identifier. 27 | These variables are used in the console output for --version and --help. 28 | */ 29 | var ( 30 | Version = "0.3.1" 31 | Build string 32 | ) 33 | 34 | func main() { 35 | configFileName := processCommandLineFlags() 36 | 37 | // Keep track of the number of services that are started 38 | serviceCounter := 0 39 | 40 | // -------------------------------------------------- 41 | // Setup logger 42 | // -------------------------------------------------- 43 | logger := log.New(os.Stderr, "", log.LstdFlags) 44 | 45 | // -------------------------------------------------- 46 | // Load System and Server Configuration 47 | // -------------------------------------------------- 48 | config, configError := config.New(logger, configFileName) 49 | if configError != nil { 50 | logger.Fatalln(configError) 51 | } 52 | 53 | // -------------------------------------------------- 54 | // Setup Logging Levels 55 | // -------------------------------------------------- 56 | switch config.Logging.Level { 57 | case 1: 58 | logger.EnableLevel("info") 59 | case 3: 60 | logger.EnableLevel("info") 61 | logger.EnableLevel("warn") 62 | case 5: 63 | logger.EnableLevel("info") 64 | logger.EnableLevel("warn") 65 | logger.EnableLevel("debug") 66 | case 10: 67 | logger.EnableLevel("info") 68 | logger.EnableLevel("warn") 69 | logger.EnableLevel("debug") 70 | logger.EnableLevel("trace") 71 | } 72 | 73 | logger.Traceln("TRACE: System Configuration Dump") 74 | logger.Tracef("%+v\n", config) 75 | 76 | // -------------------------------------------------- 77 | // Setup Logging File 78 | // -------------------------------------------------- 79 | // TODO 80 | // Need to make the directory if it does not already exist 81 | // To do this, we need to split the filename from the directory, we will want to only 82 | // take the last bit in case there is multiple directories /etc/foo/bar/stuff.log 83 | 84 | // Only enable logging to a file if it is turned on in the configuration file 85 | if config.Logging.Enabled == true { 86 | logFile, err := os.OpenFile(config.Logging.LogFile, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666) 87 | if err != nil { 88 | logger.Fatalf("ERROR: can not open file: %v", err) 89 | } 90 | defer logFile.Close() 91 | logger.SetOutput(logFile) 92 | } 93 | 94 | // -------------------------------------------------- 95 | // Setup Database Connection 96 | // -------------------------------------------------- 97 | var ds datastore.Datastorer 98 | switch config.Global.DbType { 99 | case "sqlite3": 100 | databaseFilename := config.Global.Prefix + config.Global.DbFile 101 | ds = sqlite3.New(logger, databaseFilename, config.CollectionResources) 102 | default: 103 | logger.Fatalln("ERROR: unknown database type, or no database type defined in the server global configuration") 104 | } 105 | defer ds.Close() 106 | 107 | // -------------------------------------------------- 108 | // 109 | // Configure HTTP Router 110 | // 111 | // -------------------------------------------------- 112 | 113 | router := mux.NewRouter() 114 | config.Router = router 115 | 116 | // -------------------------------------------------- 117 | // 118 | // Start Server 119 | // 120 | // -------------------------------------------------- 121 | 122 | logger.Println("Starting FreeTAXII Server Version:", Version) 123 | 124 | // -------------------------------------------------- 125 | // 126 | // Start a Discovery Service handler 127 | // 128 | // -------------------------------------------------- 129 | // This will look to see if there are any Discovery services defined in the 130 | // configuration file. If there are, loop through the list and setup handlers 131 | // for each one of them. The HandleFunc takes in a copy of the Discovery 132 | // Resource and the extra meta data that it needs to process the request. 133 | 134 | if config.DiscoveryServer.Enabled == true { 135 | for _, s := range config.DiscoveryServer.Services { 136 | if s.Enabled == true { 137 | 138 | // Configuration for this specific instance and its resource 139 | ts, _ := handlers.NewDiscoveryHandler(logger, s, config.DiscoveryResources[s.ResourceID]) 140 | 141 | logger.Infoln("Starting TAXII GET Discovery service at:", s.Path) 142 | router.HandleFunc(s.Path, ts.DiscoveryHandler).Methods("GET") 143 | serviceCounter++ 144 | } 145 | } 146 | } 147 | 148 | // -------------------------------------------------- 149 | // Start an API Root Service handler 150 | // Example: /api1/ 151 | // -------------------------------------------------- 152 | // This will look to see if there are any API Root services defined 153 | // in the config file. If there are, it will loop through the list 154 | // and setup handlers for each one of them. The HandleFunc passes in 155 | // copy of the API Root Resource and the extra meta data that it 156 | // needs to process the request. 157 | 158 | if config.APIRootServer.Enabled == true { 159 | for _, api := range config.APIRootServer.Services { 160 | if api.Enabled == true { 161 | 162 | logger.Infoln("Starting TAXII GET API Root service at:", api.Path) 163 | ts, _ := handlers.NewAPIRootHandler(logger, api, config.APIRootResources[api.ResourceID]) 164 | router.HandleFunc(api.Path, ts.APIRootHandler).Methods("GET") 165 | serviceCounter++ 166 | 167 | // Loop through the collections, if enabled and start the endpoints 168 | if api.Collections.Enabled == true { 169 | // Make a new map so we can work on a copy, this way we can 170 | // keep permissions unique per API root. 171 | colResources := make(map[string]*collections.Collection) 172 | 173 | // For each collection listed with ReadAccess add it to our local 174 | // copy called colResources and set the CanRead to true 175 | for _, c := range api.Collections.ReadAccess { 176 | if _, found := colResources[c]; !found { 177 | a := config.CollectionResources[c] 178 | colResources[c] = &a 179 | colResources[c].CanRead = true 180 | } 181 | } 182 | 183 | // For each collection listed with WriteAccess add it to our 184 | // local copy, only if it is not already found and set the 185 | // CanWrite to true 186 | for _, c := range api.Collections.WriteAccess { 187 | if _, found := colResources[c]; !found { 188 | a := config.CollectionResources[c] 189 | colResources[c] = &a 190 | } 191 | colResources[c].CanWrite = true 192 | } 193 | 194 | // Loop through all of the possible collections that are part 195 | // of this API Root and have either CanRead or CanWrite access 196 | // and add them to the Collection. This will prevent any collections 197 | // from showing up in the list if they do not have at least 198 | // read or write permissions. 199 | collections := collections.New() 200 | for key, _ := range colResources { 201 | col := colResources[key] 202 | collections.AddCollection(col) 203 | } 204 | 205 | // -------------------------------------------------- 206 | // Start a Collections Service handler 207 | // Example: /api1/collections/ 208 | // -------------------------------------------------- 209 | collectionsSrv, _ := handlers.NewCollectionsHandler(logger, api, *collections, config.Global.ServerRecordLimit) 210 | logger.Infoln("Starting TAXII GET Collections service of:", collectionsSrv.URLPath) 211 | router.HandleFunc(collectionsSrv.URLPath, collectionsSrv.CollectionsHandler).Methods("GET") 212 | 213 | // Loop through all the collections that we have identified 214 | // that should have basic read or write access. 215 | for _, collectionResourse := range colResources { 216 | 217 | // -------------------------------------------------- 218 | // Start a Collection handler 219 | // Example: /api1/collections/9cfa669c-ee94-4ece-afd2-f8edac37d8fd/ 220 | // -------------------------------------------------- 221 | // We do not need to check to see if the collection is enabled because that was already done 222 | collectionSrv, _ := handlers.NewCollectionHandler(logger, api, *collectionResourse, config.Global.ServerRecordLimit) 223 | logger.Infoln("Starting TAXII GET Collection service of:", collectionSrv.URLPath) 224 | router.HandleFunc(collectionSrv.URLPath, collectionSrv.CollectionHandler).Methods("GET") 225 | 226 | // -------------------------------------------------- 227 | // Start an Objects handler 228 | // Example: /api1/collections/9cfa669c-ee94-4ece-afd2-f8edac37d8fd/objects/ 229 | // -------------------------------------------------- 230 | srvObjects, _ := handlers.NewObjectsHandler(logger, api, collectionResourse.ID, config.Global.ServerRecordLimit) 231 | srvObjects.DS = ds 232 | 233 | if collectionResourse.CanRead == true { 234 | logger.Infoln("Starting TAXII GET Object service of:", srvObjects.URLPath) 235 | config.Router.HandleFunc(srvObjects.URLPath, srvObjects.STIXContentServerHandler).Methods("GET") 236 | } 237 | 238 | if collectionResourse.CanWrite == true { 239 | logger.Infoln("Starting TAXII POST Object service of:", srvObjects.URLPath) 240 | config.Router.HandleFunc(srvObjects.URLPath, srvObjects.ObjectsServerWriteHandler).Methods("POST") 241 | } 242 | 243 | // -------------------------------------------------- 244 | // Start a Objects by ID handlers 245 | // Example: /api1/collections/9cfa669c-ee94-4ece-afd2-f8edac37d8fd/objects/{objectid}/ 246 | // -------------------------------------------------- 247 | srvObjectsByID, _ := handlers.NewObjectsByIDHandler(logger, api, collectionResourse.ID, config.Global.ServerRecordLimit) 248 | srvObjectsByID.DS = ds 249 | 250 | if collectionResourse.CanRead == true { 251 | logger.Infoln("Starting TAXII GET Object by ID service of:", srvObjectsByID.URLPath) 252 | config.Router.HandleFunc(srvObjectsByID.URLPath, srvObjectsByID.STIXContentServerHandler).Methods("GET") 253 | } 254 | 255 | // -------------------------------------------------- 256 | // Start a Objects by ID Versions handlers 257 | // Example: /api1/collections/9cfa669c-ee94-4ece-afd2-f8edac37d8fd/objects/{objectid}/versions/ 258 | // -------------------------------------------------- 259 | srvObjectVersions, _ := handlers.NewObjectVersionsHandler(logger, api, collectionResourse.ID, config.Global.ServerRecordLimit) 260 | srvObjectVersions.DS = ds 261 | 262 | if collectionResourse.CanRead == true { 263 | logger.Infoln("Starting TAXII GET Object Versions service of:", srvObjectVersions.URLPath) 264 | config.Router.HandleFunc(srvObjectVersions.URLPath, srvObjectVersions.STIXContentServerHandler).Methods("GET") 265 | } 266 | 267 | // -------------------------------------------------- 268 | // Start a Manifest handler 269 | // Example: /api1/collections/9cfa669c-ee94-4ece-afd2-f8edac37d8fd/manifest/ 270 | // -------------------------------------------------- 271 | srvManifest, _ := handlers.NewManifestHandler(logger, api, collectionResourse.ID, config.Global.ServerRecordLimit) 272 | srvManifest.DS = ds 273 | 274 | if collectionResourse.CanRead == true { 275 | logger.Infoln("Starting TAXII GET Manifest service of:", srvManifest.URLPath) 276 | config.Router.HandleFunc(srvManifest.URLPath, srvManifest.STIXContentServerHandler).Methods("GET") 277 | } 278 | 279 | } // End for loop api.Collections.ResourceIDs 280 | } // End if Collections.Enabled == true 281 | } // End if api.Enabled == true 282 | } // End for loop API Root Services 283 | } // End if APIRootServer.Enabled == true 284 | 285 | // -------------------------------------------------- 286 | // 287 | // Fail if no services are running 288 | // 289 | // -------------------------------------------------- 290 | 291 | if serviceCounter == 0 { 292 | logger.Fatalln("No TAXII services defined") 293 | } 294 | 295 | // -------------------------------------------------- 296 | // 297 | // Listen for Incoming Connections 298 | // 299 | // -------------------------------------------------- 300 | 301 | if config.Global.Protocol == "http" { 302 | logger.Infoln("Listening on:", config.Global.Listen) 303 | logger.Fatalln(http.ListenAndServe(config.Global.Listen, router)) 304 | } else if config.Global.Protocol == "https" { 305 | // -------------------------------------------------- 306 | // Configure TLS settings 307 | // -------------------------------------------------- 308 | // TODO move TLS elements to configuration file 309 | tlsConfig := &tls.Config{ 310 | MinVersion: tls.VersionTLS12, 311 | CurvePreferences: []tls.CurveID{tls.CurveP521, tls.CurveP384, tls.CurveP256}, 312 | PreferServerCipherSuites: true, 313 | CipherSuites: []uint16{ 314 | tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, 315 | tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, 316 | tls.TLS_RSA_WITH_AES_256_GCM_SHA384, 317 | tls.TLS_RSA_WITH_AES_256_CBC_SHA, 318 | }, 319 | } 320 | tlsServer := &http.Server{ 321 | Addr: config.Global.Listen, 322 | Handler: router, 323 | TLSConfig: tlsConfig, 324 | TLSNextProto: make(map[string]func(*http.Server, *tls.Conn, http.Handler), 0), 325 | } 326 | 327 | tlsKeyPath := "etc/tls/" + config.Global.TLSKey 328 | tlsCrtPath := "etc/tls/" + config.Global.TLSCrt 329 | logger.Fatalln(tlsServer.ListenAndServeTLS(tlsCrtPath, tlsKeyPath)) 330 | } else { 331 | logger.Fatalln("No valid protocol was defined in the configuration file") 332 | } // end if statement 333 | } 334 | 335 | // -------------------------------------------------- 336 | // 337 | // Private functions 338 | // 339 | // -------------------------------------------------- 340 | 341 | /* 342 | processCommandLineFlags - This function will process the command line flags 343 | and will print the version or help information as needed. 344 | */ 345 | func processCommandLineFlags() string { 346 | defaultServerConfigFilename := "etc/freetaxii.conf" 347 | sOptServerConfigFilename := getopt.StringLong("config", 'c', defaultServerConfigFilename, "System Configuration File", "string") 348 | bOptHelp := getopt.BoolLong("help", 0, "Help") 349 | bOptVer := getopt.BoolLong("version", 0, "Version") 350 | 351 | getopt.HelpColumn = 35 352 | getopt.DisplayWidth = 120 353 | getopt.SetParameters("") 354 | getopt.Parse() 355 | 356 | // Lets check to see if the version command line flag was given. If it is 357 | // lets print out the version infomration and exit. 358 | if *bOptVer { 359 | printOutputHeader() 360 | os.Exit(0) 361 | } 362 | 363 | // Lets check to see if the help command line flag was given. If it is lets 364 | // print out the help information and exit. 365 | if *bOptHelp { 366 | printOutputHeader() 367 | getopt.Usage() 368 | os.Exit(0) 369 | } 370 | return *sOptServerConfigFilename 371 | } 372 | 373 | /* 374 | printOutputHeader - This function will print a header for all console output 375 | */ 376 | func printOutputHeader() { 377 | fmt.Println("") 378 | fmt.Println("FreeTAXII Server") 379 | fmt.Println("Copyright: Bret Jordan") 380 | fmt.Println("Version:", Version) 381 | if Build != "" { 382 | fmt.Println("Build:", Build) 383 | } 384 | fmt.Println("") 385 | } 386 | -------------------------------------------------------------------------------- /cmd/freetaxii/templates/README.md: -------------------------------------------------------------------------------- 1 | # FreeTAXII/freetaxii-server/html # 2 | 3 | The FreeTAXII-Server is a TAXII 2 Server written in Go (golang) 4 | 5 | 6 | ## HTML Template Files Variables ## 7 | 8 | Each of the HTML template files can be customized as needed per organizational 9 | requirements and supports the variables as defined below. The freetaxii.conf file 10 | allows template files to be defined globally and also redefined at a specific 11 | instance. For example, one can define an HTML template for all of the API roots 12 | that are found on the server and can also override that template for a specific 13 | API root. This would allow each API root to have its own template and branding. 14 | 15 | 16 | ## HTML Configuration ## 17 | 18 | ``` 19 | "html" : { 20 | "enabled" : true, 21 | "templatedir" : "templates/html/", 22 | "templatefiles" : { 23 | "discovery" : "discoveryResource.html", 24 | "apiroot" : "apirootResource.html", 25 | "collections" : "collectionsResource.html", 26 | "collection" : "collectionResource.html", 27 | "objects" : "objectsResource.html", 28 | "versions" : "versionsResource.html", 29 | "manifest" : "manifestResource.html" 30 | } 31 | } 32 | ``` 33 | 34 | 35 | ## License ## 36 | 37 | This is free software, licensed under the Apache License, Version 2.0. 38 | 39 | 40 | ## Copyright ## 41 | 42 | Copyright 2015-2018 Bret Jordan, All rights reserved. -------------------------------------------------------------------------------- /cmd/freetaxii/templates/html/apirootResource.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | FreeTAXII - API Root Service 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
FreeTAXII - A TAXII 2 Server
17 |

 

18 |

API Root Service

19 | 20 |

Path: {{ .URLPath }}
21 | Collections URL: {{ .URLPath }}collections/
22 |


23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | {{if .Resource.Description}} 31 | 32 | 33 | 34 | 35 | {{end}} 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 |
Title:{{ .Resource.Title }}
Description:{{ .Resource.Description }}
Versions:{{ range .Resource.Versions }} {{ . }}
{{ end }}
Max Content Length:{{ .Resource.MaxContentLength }}
46 |

 

47 |
48 |
Copyright 2017 - Bret Jordan
49 | 50 | 51 | -------------------------------------------------------------------------------- /cmd/freetaxii/templates/html/collectionResource.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | FreeTAXII - Collection Service 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
FreeTAXII - A TAXII 2 Server
17 |

 

18 |

Collection Service

19 | 20 |

Path: {{ .URLPath }}
21 | Objects URL: {{ .URLPath }}objects/
22 |


23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | {{if .Resource.Description}} 38 | 39 | 40 | 41 | 42 | {{end}} 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 |
ID:{{ .Resource.ID }}
Title:{{ .Resource.Title }}
Description:{{ .Resource.Description }}
Can Read:{{ .Resource.CanRead }}
Can Write:{{ .Resource.CanWrite }}
Media Types:{{ range .Resource.MediaTypes }} {{ . }}
{{ end }}
57 |

 

58 |
59 |
Copyright 2017 - Bret Jordan
60 | 61 | 62 | -------------------------------------------------------------------------------- /cmd/freetaxii/templates/html/collectionsResource.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | FreeTAXII - Collections Service 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
FreeTAXII - A TAXII 2 Server
17 |

 

18 |

Collections Service

19 | 20 |

Path: {{ .URLPath }}
21 |


22 | 23 | 24 | 25 | {{ range .Resource.Collections }} 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | {{ end }} 52 | 53 |
Title: {{ .Title }}
ID: {{ .ID }}
Read: {{ .CanRead }}
Write: {{ .CanWrite }}
Media Types:
{{ range .MediaTypes }} {{ . }}
{{ end }}
Description:
{{ .Description }}

54 |

 

55 |
56 |
Copyright 2017 - Bret Jordan
57 | 58 | 59 | -------------------------------------------------------------------------------- /cmd/freetaxii/templates/html/discoveryResource.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | FreeTAXII - Discovery Service 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
FreeTAXII - A TAXII 2 Server
17 |

 

18 |

Discovery Service

19 | 20 |

Path: {{ .URLPath }}
21 |


22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | {{if .Resource.Description}} 30 | 31 | 32 | 33 | 34 | {{end}} 35 | {{if .Resource.Contact}} 36 | 37 | 38 | 39 | 40 | {{end}} 41 | {{if .Resource.Default}} 42 | 43 | 44 | 45 | 46 | {{end}} 47 | 48 | 49 | 50 | 51 | 52 |
Title:{{ .Resource.Title }}
Description:{{ .Resource.Description }}
Contact:{{ .Resource.Contact }}
Default API Root:{{ .Resource.Default }}
API Roots:{{ range .Resource.APIRoots }} {{ . }}
{{ end }}
53 |

 

54 |
55 |
Copyright 2017 - Bret Jordan
56 | 57 | 58 | -------------------------------------------------------------------------------- /cmd/freetaxii/templates/html/manifestResource.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | FreeTAXII - Manifest Service 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
FreeTAXII - A TAXII 2 Server
17 |

 

18 |

Manifest Service

19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 |
Path:{{ .URLPath }}

TAXII Manifest:
{{ .Resource }}
34 |

 

35 |
36 |
Copyright 2017 - Bret Jordan
37 | 38 | 39 | -------------------------------------------------------------------------------- /cmd/freetaxii/templates/html/objectsResource.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | FreeTAXII - Objects Service 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
FreeTAXII - A TAXII 2 Server
17 |

 

18 |

Objects Service

19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 |
Path:{{ .URLPath }}

TAXII Envelope:
{{ .Resource }}
34 |

 

35 |
36 |
Copyright 2017 - Bret Jordan
37 | 38 | 39 | -------------------------------------------------------------------------------- /cmd/freetaxii/templates/html/versionsResource.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | FreeTAXII - Objects Service 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
FreeTAXII - A TAXII 2 Server
17 |

 

18 |

Object Versions Service

19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 |
Path:{{ .URLPath }}

Versions:
{{ .Resource }}
34 |

 

35 |
36 |
Copyright 2017 - Bret Jordan
37 | 38 | 39 | -------------------------------------------------------------------------------- /cmd/verifyconfig/verifyconfig.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015-2018 Bret Jordan, All rights reserved. 2 | // 3 | // Use of this source code is governed by an Apache 2.0 license 4 | // that can be found in the LICENSE file in the root of the source tree. 5 | 6 | package main 7 | 8 | import ( 9 | "fmt" 10 | "os" 11 | 12 | "github.com/freetaxii/server/internal/config" 13 | "github.com/gologme/log" 14 | "github.com/pborman/getopt" 15 | ) 16 | 17 | // These global variables hold build information. The Build variable will be 18 | // populated by the Makefile and uses the Git Head hash as its identifier. 19 | // These variables are used in the console output for --version and --help. 20 | var ( 21 | Version = "0.3.1" 22 | Build string 23 | ) 24 | 25 | // These global variables are for dealing with command line options 26 | var ( 27 | defaultServerConfigFilename = "../etc/freetaxii.conf" 28 | sOptServerConfigFilename = getopt.StringLong("config", 'c', defaultServerConfigFilename, "System Configuration File", "string") 29 | bOptHelp = getopt.BoolLong("help", 0, "Help") 30 | bOptVer = getopt.BoolLong("version", 0, "Version") 31 | ) 32 | 33 | func main() { 34 | processCommandLineFlags() 35 | 36 | logger := log.New(os.Stderr, "", log.LstdFlags) 37 | logger.EnableLevel("info") 38 | logger.EnableLevel("debug") 39 | 40 | // -------------------------------------------------- 41 | // Define variables 42 | // -------------------------------------------------- 43 | 44 | _, err := config.New(logger, *sOptServerConfigFilename) 45 | 46 | if err != nil { 47 | logger.Fatalln(err) 48 | } 49 | 50 | // -------------------------------------------------- 51 | // Load System and Server Configuration 52 | // -------------------------------------------------- 53 | 54 | logger.Println("No errors found") 55 | } 56 | 57 | // -------------------------------------------------- 58 | // Private functions 59 | // -------------------------------------------------- 60 | 61 | // processCommandLineFlags - This function will process the command line flags 62 | // and will print the version or help information as needed. 63 | func processCommandLineFlags() { 64 | getopt.HelpColumn = 35 65 | getopt.DisplayWidth = 120 66 | getopt.SetParameters("") 67 | getopt.Parse() 68 | 69 | // Lets check to see if the version command line flag was given. If it is 70 | // lets print out the version infomration and exit. 71 | if *bOptVer { 72 | printOutputHeader() 73 | os.Exit(0) 74 | } 75 | 76 | // Lets check to see if the help command line flag was given. If it is lets 77 | // print out the help information and exit. 78 | if *bOptHelp { 79 | printOutputHeader() 80 | getopt.Usage() 81 | os.Exit(0) 82 | } 83 | } 84 | 85 | // printOutputHeader - This function will print a header for all console output 86 | func printOutputHeader() { 87 | fmt.Println("") 88 | fmt.Println("FreeTAXII Server") 89 | fmt.Println("Copyright, Bret Jordan") 90 | fmt.Println("Version:", Version) 91 | if Build != "" { 92 | fmt.Println("Build:", Build) 93 | } 94 | fmt.Println("") 95 | } 96 | -------------------------------------------------------------------------------- /internal/config/jsonTypes.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015-2018 Bret Jordan, All rights reserved. 2 | // 3 | // Use of this source code is governed by an Apache 2.0 license 4 | // that can be found in the LICENSE file in the root of the source 5 | // tree. 6 | 7 | package config 8 | 9 | import ( 10 | "encoding/json" 11 | ) 12 | 13 | /* 14 | JSONbool - A boolean type for use with JSON that will track if a value is 15 | undefined or set to null. 16 | */ 17 | type JSONbool struct { 18 | Value bool 19 | Valid bool 20 | Set bool 21 | } 22 | 23 | /* 24 | JSONstring - A string type for use with JSON that will track if a value is 25 | undefined or set to null. 26 | */ 27 | type JSONstring struct { 28 | Value string 29 | Valid bool 30 | Set bool 31 | } 32 | 33 | /* 34 | UnmarshalJSON - This method defines the unmarshal process for the JSONbool type. 35 | We need to track of the value was set, set to null, or actually set to true or 36 | false. This is important since not being set is not the same thing has false or 37 | true. This comes into play when you need to inherit some previous higher level 38 | setting. Meaning if you set a property higher up in the configuration to true or 39 | false, then you want that to be cascaded down and without doing this, it does 40 | not work. 41 | */ 42 | func (b *JSONbool) UnmarshalJSON(data []byte) error { 43 | // If this method was called, the value was set. 44 | b.Set = true 45 | 46 | if string(data) == "null" { 47 | // The key was set to null 48 | b.Valid = false 49 | b.Value = false 50 | return nil 51 | } 52 | 53 | // The key isn't set to null 54 | var temp bool 55 | if err := json.Unmarshal(data, &temp); err != nil { 56 | return err 57 | } 58 | b.Value = temp 59 | b.Valid = true 60 | return nil 61 | } 62 | 63 | /* 64 | UnmarshalJSON - This method will handle the unmarshalling of content for the 65 | JSONbool type 66 | */ 67 | func (s *JSONstring) UnmarshalJSON(data []byte) error { 68 | // If this method was called, the value was set. 69 | s.Set = true 70 | 71 | if string(data) == "null" { 72 | // The key was set to null 73 | s.Valid = false 74 | s.Value = "" 75 | return nil 76 | } 77 | 78 | // The key isn't set to null 79 | var temp string 80 | if err := json.Unmarshal(data, &temp); err != nil { 81 | return err 82 | } 83 | s.Value = temp 84 | s.Valid = true 85 | return nil 86 | } 87 | -------------------------------------------------------------------------------- /internal/config/jsonTypes_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015-2018 Bret Jordan, All rights reserved. 2 | // 3 | // Use of this source code is governed by an Apache 2.0 license that can be 4 | // found in the LICENSE file in the root of the source tree. 5 | 6 | package config 7 | 8 | import ( 9 | "encoding/json" 10 | "testing" 11 | ) 12 | 13 | type Dataset struct { 14 | HTMLConfigType 15 | Service1 HTMLConfigType 16 | Service2 HTMLConfigType 17 | Service3 HTMLConfigType 18 | } 19 | 20 | // type HTMLConfigType struct { 21 | // Enabled JSONbool // User defined in configuration file or set in verifyHTMLConfig() 22 | // TemplateDir JSONstring // User defined in configuration file or set in verifyHTMLConfig() 23 | // TemplatePath JSONstring // Set in verifyHTMLConfig() 24 | // TemplateFiles struct { 25 | // Discovery JSONstring // User defined in configuration file or set in verifyHTMLConfig() 26 | // APIRoot JSONstring 27 | // Collections JSONstring 28 | // Collection JSONstring 29 | // Objects JSONstring 30 | // Manifest JSONstring 31 | // } 32 | // } 33 | 34 | var data = ` 35 | { 36 | "Enabled": true, 37 | "TemplateDir": "html", 38 | "TemplatePath": "/foo/", 39 | "TemplateFiles": { 40 | "Discovery": "d1", 41 | "APIRoot": "a1" 42 | "Collections": "cols1", 43 | "Collection": "col1", 44 | "Objects": "o1", 45 | "Manifest": "m1" 46 | }, 47 | "Service1": { 48 | "Enabled": true, 49 | "TemplateDir": "html", 50 | "TemplatePath": "/bar/", 51 | "TemplateFiles": { 52 | "Discovery": "d1", 53 | "APIRoot": "a2" 54 | "Collections": "cols1", 55 | "Collection": "col1", 56 | "Objects": "o2", 57 | "Manifest": "m1" 58 | } 59 | }, 60 | "Service2": { 61 | "Enabled": false, 62 | "TemplateDir": "html", 63 | "TemplatePath": "/bar/", 64 | "TemplateFiles": { 65 | "Discovery": "d1", 66 | "APIRoot": "a2" 67 | "Collections": "cols1", 68 | "Collection": "col1", 69 | "Objects": "o2", 70 | "Manifest": "m1" 71 | } 72 | }, 73 | "Service3": { 74 | "TemplateFiles": { 75 | "Discovery": "d3" 76 | } 77 | } 78 | }` 79 | 80 | // ---------------------------------------------------------------------- 81 | func Test_HTMLConfigType(t *testing.T) { 82 | var c Dataset 83 | 84 | decoder := json.NewDecoder(data) 85 | err := decoder.Decode(c) 86 | 87 | if err != nil { 88 | return t.Errorf("error parsing the configuration file: %v", err) 89 | } 90 | 91 | // t.Log("Test 1: get an error for no collection id") 92 | // if _, err := sqlGetObjectList(query); err == nil { 93 | // t.Error("no error returned") 94 | // } 95 | 96 | // t.Log("Test 2: get correct sql statement for object list") 97 | // query.CollectionID = "aa" 98 | // testdata = `SELECT t_collection_data.date_added, t_collection_data.stix_id, s_base_object.modified, s_base_object.spec_version FROM t_collection_data JOIN s_base_object ON t_collection_data.stix_id = s_base_object.id WHERE t_collection_data.collection_id = "aa"` 99 | // if v, _ := sqlGetObjectList(query); testdata != v { 100 | // t.Error("sql statement is not correct") 101 | // } 102 | } 103 | -------------------------------------------------------------------------------- /internal/config/serverConfig.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015-2018 Bret Jordan, All rights reserved. 2 | // 3 | // Use of this source code is governed by an Apache 2.0 license 4 | // that can be found in the LICENSE file in the root of the source tree. 5 | 6 | package config 7 | 8 | import ( 9 | "encoding/json" 10 | "fmt" 11 | "os" 12 | 13 | "github.com/freetaxii/libstix2/resources/apiroot" 14 | "github.com/freetaxii/libstix2/resources/collections" 15 | "github.com/freetaxii/libstix2/resources/discovery" 16 | "github.com/gologme/log" 17 | "github.com/gorilla/mux" 18 | ) 19 | 20 | /* 21 | ServerConfig - This type defines the configuration for the entire server. 22 | */ 23 | type ServerConfig struct { 24 | Router *mux.Router 25 | Logger *log.Logger 26 | Global struct { 27 | Prefix string 28 | Listen string 29 | Protocol string 30 | TLSDir string 31 | TLSKey string 32 | TLSCrt string 33 | DbConfig bool 34 | DbType string 35 | DbFile string 36 | ServerRecordLimit int 37 | } 38 | HTML struct { 39 | HTMLConfig 40 | } 41 | Logging struct { 42 | Enabled bool 43 | Level int 44 | LogFile string 45 | } 46 | DiscoveryServer struct { 47 | Enabled bool 48 | Services []DiscoveryService 49 | } `json:"discovery_server,omitempty"` 50 | APIRootServer struct { 51 | Enabled bool 52 | Services []APIRootService 53 | } `json:"apiroot_server,omitempty"` 54 | DiscoveryResources map[string]discovery.Discovery `json:"discovery_resources,omitempty"` // The key in the map is the ResourceID 55 | APIRootResources map[string]apiroot.APIRoot `json:"apiroot_resources,omitempty"` // The key in the map is the ResourceID 56 | CollectionResources map[string]collections.Collection `json:"collection_resources,omitempty"` // The key in the map is the ResourceID 57 | } 58 | 59 | /* 60 | BaseService - This struct represents the common properties between the 61 | Discovery and API-Root services. 62 | 63 | Path - The URL path for this service 64 | Enabled - Is this service enabled 65 | ResourceID - A unique ID for the resource that this service is using 66 | ResourcePath - The actual full URL path for the resource, used for the handler to know where to listen. 67 | HTML - The configuration for generating HTML output 68 | */ 69 | type BaseService struct { 70 | Enabled bool // User defined in configuration file 71 | Path string // User defined in configuration file 72 | ResourceID string // User defined in configuration file 73 | HTML HTMLConfig // User defined in configuration file or set in the verify scripts. 74 | } 75 | 76 | /* 77 | DiscoveryService - This struct represents an instance of a Discovery server. 78 | If someone tries to set the 'resourcepath' directive in the configuration file it 79 | will get overwritten in code. 80 | */ 81 | type DiscoveryService struct { 82 | BaseService 83 | } 84 | 85 | /* 86 | APIRootService - This struct represents an instance of an API Root server. 87 | If someone tries to set the 'path' directive in the configuration file it 88 | will just get overwritten in code. 89 | ReadAccess - This is a list of collection resource IDs that may have GET access 90 | at the API Root level 91 | WriteAccess - This is a list of collection resource IDs that may have POST access 92 | at the API Root level 93 | */ 94 | type APIRootService struct { 95 | BaseService 96 | Collections struct { 97 | Enabled bool // User defined in configuration file 98 | ReadAccess []string // User defined in configuration file. 99 | WriteAccess []string // User defined in configuration file. 100 | } 101 | } 102 | 103 | /* 104 | HTMLConfig - This struct holds the configuration elements for generating HTML 105 | output. This is used at the top level of the configuration file as well as in 106 | each individual service. This means individual services can have a different 107 | HTML configuration. I needed to setup my own types for JSON boolean and strings 108 | since leaving it blank at a child level, would have equaled "false" or "". This 109 | would have been equivalent to turning it off, which is not what is wanted. Leaving 110 | it blank would mean to inherit from the parent. But since Go is a strictly typed 111 | language, you need to create a type that can handle that case. 112 | 113 | Enabled - Is HTML enabled for this service 114 | TemplateDir - The location of the template files relative to the base of the application (prefix) 115 | FullTemplatePath - The full path of the template directory (prefix + TemplateDir) 116 | TemplateFiles - The HTML template filenames in the template directory for the following services 117 | */ 118 | type HTMLConfig struct { 119 | Enabled JSONbool // User defined in configuration file or set in verifyHTMLConfig() 120 | TemplateDir JSONstring // User defined in configuration file or set in verifyHTMLConfig() 121 | TemplateFiles struct { 122 | Discovery JSONstring // User defined in configuration file or set in verifyHTMLConfig() 123 | APIRoot JSONstring 124 | Collections JSONstring 125 | Collection JSONstring 126 | Objects JSONstring 127 | Versions JSONstring 128 | Manifest JSONstring 129 | } 130 | FullTemplatePath string // Set in verifyHTMLConfig(), this is the full path to template files 131 | } 132 | 133 | // ---------------------------------------------------------------------- 134 | // 135 | // Public Create Functions 136 | // 137 | // ---------------------------------------------------------------------- 138 | 139 | /* 140 | New - This function will load the current configuration from a file, verify that 141 | the configuration is correct, and then return a ServerConfig type. 142 | */ 143 | func New(logger *log.Logger, filename string) (ServerConfig, error) { 144 | var c ServerConfig 145 | var err error 146 | 147 | if logger == nil { 148 | c.Logger = log.New(os.Stderr, "", log.LstdFlags) 149 | } else { 150 | c.Logger = logger 151 | } 152 | 153 | err = c.loadServerConfig(filename) 154 | if err != nil { 155 | return c, err 156 | } 157 | 158 | // In addition to checking the configuration for completeness the verify 159 | // process will also populate some of the helper values. 160 | err = c.Verify() 161 | if err != nil { 162 | return c, err 163 | } 164 | return c, nil 165 | } 166 | 167 | // -------------------------------------------------- 168 | // 169 | // Load Configuration File, Parse JSON, and Verify 170 | // 171 | // -------------------------------------------------- 172 | 173 | /* 174 | loadServerConfig - This methods takes in a string value representing a 175 | filename of the configuration file and loads the configuration into memory. 176 | */ 177 | func (c *ServerConfig) loadServerConfig(filename string) error { 178 | // TODO - Need to make make a validation check for the configuration file 179 | 180 | // Open and read configuration file 181 | sysConfigFileData, err1 := os.Open(filename) 182 | defer sysConfigFileData.Close() 183 | if err1 != nil { 184 | return fmt.Errorf("error opening configuration file: %v", err1) 185 | } 186 | 187 | // -------------------------------------------------- 188 | // Decode JSON configuration file 189 | // -------------------------------------------------- 190 | // Use decoder instead of unmarshal so we can handle stream data 191 | decoder := json.NewDecoder(sysConfigFileData) 192 | err2 := decoder.Decode(c) 193 | 194 | if err2 != nil { 195 | return fmt.Errorf("error parsing the configuration file: %v", err2) 196 | } 197 | 198 | return nil 199 | } 200 | 201 | /* 202 | exists - This method checks to see if the filename exists on the file system. 203 | This is used by several of the configuration directive checks, basically anytime 204 | there is a filename defined in the configuration file, this is called to check 205 | to see if that file actually exists on the file system. 206 | */ 207 | func (c *ServerConfig) exists(name string) bool { 208 | if _, err := os.Stat(name); err != nil { 209 | if os.IsNotExist(err) { 210 | return false 211 | } 212 | return false 213 | } 214 | return true 215 | } 216 | -------------------------------------------------------------------------------- /internal/config/verify.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015-2018 Bret Jordan, All rights reserved. 2 | // 3 | // Use of this source code is governed by an Apache 2.0 license 4 | // that can be found in the LICENSE file in the root of the source tree. 5 | 6 | package config 7 | 8 | import ( 9 | "errors" 10 | ) 11 | 12 | /* 13 | Verify - This method will verify that the configuration file has what it needs. 14 | TODO finish fleshing this out 15 | */ 16 | func (c *ServerConfig) Verify() error { 17 | var problemsFound = 0 18 | 19 | // -------------------------------------------------- 20 | // Global Configuration 21 | // -------------------------------------------------- 22 | problemsFound += c.verifyGlobalConfig() 23 | 24 | // -------------------------------------------------- 25 | // Global HTML Configuration 26 | // -------------------------------------------------- 27 | // If HTML output is turned off globally, then there no need to check the 28 | // configuration and verify everything is present and valid. 29 | if c.HTML.Enabled.Value == true { 30 | problemsFound += c.verifyGlobalHTMLConfig() 31 | } 32 | 33 | // -------------------------------------------------- 34 | // Discovery Server 35 | // -------------------------------------------------- 36 | // Only verify the Discovery server configuration if it is enabled. 37 | if c.DiscoveryServer.Enabled == true { 38 | problemsFound += c.verifyDiscoveryConfig() 39 | 40 | if c.HTML.Enabled.Value == true { 41 | problemsFound += c.verifyDiscoveryHTMLConfig() 42 | } 43 | } 44 | 45 | // -------------------------------------------------- 46 | // API Root Server 47 | // -------------------------------------------------- 48 | // Only verify the API Root server configuration if it is enabled. 49 | if c.APIRootServer.Enabled == true { 50 | problemsFound += c.verifyAPIRootConfig() 51 | 52 | if c.HTML.Enabled.Value == true { 53 | problemsFound += c.verifyAPIRootHTMLConfig() 54 | } 55 | } 56 | 57 | if problemsFound > 0 { 58 | c.Logger.Println("ERROR: The configuration has", problemsFound, "error(s)") 59 | return errors.New("ERROR: Configuration errors found") 60 | } 61 | return nil 62 | } 63 | -------------------------------------------------------------------------------- /internal/config/verifyGlobalConfig.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015-2018 Bret Jordan, All rights reserved. 2 | // 3 | // Use of this source code is governed by an Apache 2.0 license 4 | // that can be found in the LICENSE file in the root of the source tree. 5 | 6 | package config 7 | 8 | import ( 9 | "strings" 10 | ) 11 | 12 | /* 13 | verifyGlobalConfig - This method will verify that each required configuration 14 | directive is present and will return the number of errors found. 15 | */ 16 | func (c *ServerConfig) verifyGlobalConfig() int { 17 | var problemsFound = 0 18 | 19 | // Protocol Directive 20 | if c.Global.Protocol != "https" && c.Global.Protocol != "http" { 21 | c.Logger.Println("CONFIG: The global.protocol directive must be either https or http") 22 | problemsFound++ 23 | } 24 | 25 | // TLS Files - only needed if https is defined 26 | if c.Global.Protocol == "https" { 27 | problemsFound += c.verifyTLSConfig() 28 | } 29 | 30 | // Listen Directive 31 | if c.Global.Listen == "" { 32 | c.Logger.Println("CONFIG: The global.listen directive is missing from the configuration file") 33 | problemsFound++ 34 | } 35 | 36 | // Prefix Directive 37 | if c.Global.Prefix == "" { 38 | c.Logger.Println("CONFIG: The global.prefix directive is missing from the configuration file") 39 | problemsFound++ 40 | } else { 41 | if !strings.HasSuffix(c.Global.Prefix, "/") { 42 | c.Logger.Println("CONFIG: The global.prefix directive is missing the ending slash '/'") 43 | problemsFound++ 44 | } 45 | } 46 | 47 | // Database Configuration Directive 48 | if c.Global.DbConfig == true && c.Global.DbFile == "" { 49 | c.Logger.Println("CONFIG: The global.dbconfig directive is set to true, however, the global.dbfile directive is missing from the configuration file") 50 | problemsFound++ 51 | } 52 | 53 | // Logging File 54 | if c.Logging.Enabled == true && c.Logging.LogFile == "" { 55 | c.Logger.Println("CONFIG: The logging.logfile directive is missing from the configuration file") 56 | problemsFound++ 57 | } 58 | 59 | // ---------------------------------------------------------------------- 60 | // Return number of errors if there are any 61 | // ---------------------------------------------------------------------- 62 | if problemsFound > 0 { 63 | c.Logger.Println("ERROR: The Global configuration has", problemsFound, "error(s)") 64 | } 65 | return problemsFound 66 | } 67 | 68 | /* 69 | verifyTLSConfig - This method will verify that each required TLS configuration 70 | directive are present. 71 | */ 72 | func (c *ServerConfig) verifyTLSConfig() int { 73 | var problemsFound = 0 74 | 75 | if c.Global.TLSDir == "" { 76 | c.Logger.Println("CONFIG: The global.tlsdir directive is missing from the configuration file") 77 | problemsFound++ 78 | } else { 79 | filepath := c.Global.Prefix + c.Global.TLSDir 80 | 81 | if !strings.HasSuffix(c.Global.TLSDir, "/") { 82 | c.Logger.Println("CONFIG: The global.tlsdir directive is missing the ending slash '/'") 83 | problemsFound++ 84 | } 85 | 86 | if !c.exists(filepath) { 87 | c.Logger.Println("CONFIG: The TLS path", filepath, "can not be opened") 88 | problemsFound++ 89 | } 90 | } 91 | 92 | if c.Global.TLSCrt == "" { 93 | c.Logger.Println("CONFIG: The global.tlscrt directive is missing from the configuration file") 94 | problemsFound++ 95 | } else { 96 | file := c.Global.Prefix + c.Global.TLSDir + c.Global.TLSCrt 97 | if !c.exists(file) { 98 | c.Logger.Println("CONFIG: The TLS Cert file", file, "can not be opened") 99 | problemsFound++ 100 | } 101 | } 102 | 103 | if c.Global.TLSKey == "" { 104 | c.Logger.Println("CONFIG: The global.tlskey directive is missing from the configuration file") 105 | problemsFound++ 106 | } else { 107 | file := c.Global.Prefix + c.Global.TLSDir + c.Global.TLSKey 108 | if !c.exists(file) { 109 | c.Logger.Println("CONFIG: The TLS Key file", file, "can not be opened") 110 | problemsFound++ 111 | } 112 | } 113 | 114 | return problemsFound 115 | } 116 | -------------------------------------------------------------------------------- /internal/config/verifyHTMLConfig.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015-2018 Bret Jordan, All rights reserved. 2 | // 3 | // Use of this source code is governed by an Apache 2.0 license 4 | // that can be found in the LICENSE file in the root of the source tree. 5 | 6 | package config 7 | 8 | import ( 9 | "strconv" 10 | "strings" 11 | ) 12 | 13 | // ---------------------------------------- 14 | // Verify Discovery HTML Files 15 | // ---------------------------------------- 16 | 17 | /* 18 | verifyGlobalHTMLConfig - This method will check each of the defined HTML settings 19 | in the global configuration and return the number of errors found. 20 | */ 21 | func (c *ServerConfig) verifyGlobalHTMLConfig() int { 22 | var problemsFound = 0 23 | 24 | // ---------------------------------------------------------------------- 25 | // Verify TemplateDir is defined and exists on the file system 26 | // ---------------------------------------------------------------------- 27 | // Check to see if the template directory is implicitly set to null ("") or 28 | // explicitly set to null ("null"). When set explicitly to null that means 29 | // it is also invalid. 30 | if c.HTML.TemplateDir.Value == "" || c.HTML.TemplateDir.Valid == false { 31 | c.Logger.Println("CONFIG: The global HTML configuration is missing the html.templatedir directive in the configuration file") 32 | problemsFound++ 33 | } else { 34 | problemsFound += c.verifyHTMLTemplateDir("html.templatedir", c.HTML.TemplateDir) 35 | c.HTML.FullTemplatePath = c.Global.Prefix + c.HTML.TemplateDir.Value 36 | } 37 | 38 | // ---------------------------------------------------------------------- 39 | // Verify actual template files are defined and exist on the file system 40 | // ---------------------------------------------------------------------- 41 | problemsFound += c.verifyGlobalHTMLTemplateFile("html.templatefiles.discovery", c.HTML.FullTemplatePath, c.HTML.TemplateFiles.Discovery) 42 | problemsFound += c.verifyGlobalHTMLTemplateFile("html.templatefiles.apiroot", c.HTML.FullTemplatePath, c.HTML.TemplateFiles.APIRoot) 43 | problemsFound += c.verifyGlobalHTMLTemplateFile("html.templatefiles.collections", c.HTML.FullTemplatePath, c.HTML.TemplateFiles.Collections) 44 | problemsFound += c.verifyGlobalHTMLTemplateFile("html.templatefiles.collection", c.HTML.FullTemplatePath, c.HTML.TemplateFiles.Collection) 45 | problemsFound += c.verifyGlobalHTMLTemplateFile("html.templatefiles.objects", c.HTML.FullTemplatePath, c.HTML.TemplateFiles.Objects) 46 | problemsFound += c.verifyGlobalHTMLTemplateFile("html.templatefiles.versions", c.HTML.FullTemplatePath, c.HTML.TemplateFiles.Versions) 47 | problemsFound += c.verifyGlobalHTMLTemplateFile("html.templatefiles.manifest", c.HTML.FullTemplatePath, c.HTML.TemplateFiles.Manifest) 48 | 49 | // ---------------------------------------------------------------------- 50 | // Return number of errors if there are any 51 | // ---------------------------------------------------------------------- 52 | if problemsFound > 0 { 53 | c.Logger.Println("ERROR: The global HTML configuration has", problemsFound, "error(s)") 54 | } 55 | return problemsFound 56 | } 57 | 58 | /* 59 | verifyHTMLTemplateDir - This method will verify that the template directory in 60 | the configuration has a trailing slash and that it is found on the file system. 61 | */ 62 | func (c *ServerConfig) verifyHTMLTemplateDir(configPath string, templateDir JSONstring) int { 63 | var problemsFound = 0 64 | 65 | if !strings.HasSuffix(templateDir.Value, "/") { 66 | c.Logger.Println("CONFIG: The" + configPath + "directive is missing the ending slash '/'") 67 | problemsFound++ 68 | } 69 | 70 | filepath := c.Global.Prefix + templateDir.Value 71 | if !c.exists(filepath) { 72 | c.Logger.Println("CONFIG: The HTML template path", filepath, "can not be opened") 73 | problemsFound++ 74 | } 75 | return problemsFound 76 | } 77 | 78 | /* 79 | verifyGlobalHTMLTemplateFile - This method will verify that the global HTML 80 | template files are defined and that they can be found on the file system. 81 | */ 82 | func (c *ServerConfig) verifyGlobalHTMLTemplateFile(configPath, templatePath string, template JSONstring) int { 83 | var problemsFound = 0 84 | 85 | if templatePath == "" { 86 | c.Logger.Println("CONFIG: The HTML template path used by" + configPath + "is missing") 87 | problemsFound++ 88 | return problemsFound 89 | } 90 | 91 | if template.Value == "" || template.Valid == false { 92 | c.Logger.Println("CONFIG: The HTML configuration is missing the", configPath, "directive in the configuration file") 93 | problemsFound++ 94 | return problemsFound 95 | } 96 | 97 | problemsFound += c.verifyHTMLTemplateFile(configPath, templatePath, template) 98 | return problemsFound 99 | } 100 | 101 | /* 102 | verifyHTMLTemplateFile - This method will verify that HTML template files are 103 | found on the file system. 104 | */ 105 | func (c *ServerConfig) verifyHTMLTemplateFile(configPath, templatePath string, template JSONstring) int { 106 | var problemsFound = 0 107 | 108 | if templatePath == "" { 109 | c.Logger.Println("CONFIG: The HTML template path used by" + configPath + "is missing") 110 | problemsFound++ 111 | return problemsFound 112 | } 113 | 114 | filepath := templatePath + template.Value 115 | if !c.exists(filepath) { 116 | c.Logger.Println("CONFIG: The HTML template path", filepath, "defined at", configPath, " can not be opened") 117 | problemsFound++ 118 | } 119 | 120 | return problemsFound 121 | } 122 | 123 | /* 124 | verifyDiscoveryHTMLConfig - This method will check each of the services to see 125 | if the HTML configuration has been redefined. If the values have not been 126 | redefined then the global settings will be copied in to this level. The actual 127 | HTTP handlers will use the settings found in these services and not the global 128 | settings. 129 | 130 | This method is called from serverConfig.go-verifyServerConfig() 131 | */ 132 | func (c *ServerConfig) verifyDiscoveryHTMLConfig() int { 133 | var problemsFound = 0 134 | 135 | // If HTML output is not enabled globally, then skip these tests 136 | if c.HTML.Enabled.Value == false { 137 | return problemsFound 138 | } 139 | 140 | // Check to see if any of the HTML configurations were redefined at each service level 141 | for i, s := range c.DiscoveryServer.Services { 142 | indexString := strconv.Itoa(i) 143 | 144 | // Check to see if the following values were redefined and valid. If 145 | // they were not redefined or they are invalid (set to "null" and thus 146 | // invalid) then lets set them to the same as the global configuration. 147 | // Copy all of the settings for the object, not just the value. 148 | // If the value was redefined and is valid, lets just leave it alone. 149 | if s.HTML.Enabled.Set == false || s.HTML.Enabled.Valid == false { 150 | c.DiscoveryServer.Services[i].HTML.Enabled = c.HTML.Enabled 151 | } 152 | 153 | if s.HTML.TemplateDir.Set == false || s.HTML.TemplateDir.Valid == false { 154 | c.DiscoveryServer.Services[i].HTML.TemplateDir = c.HTML.TemplateDir 155 | c.DiscoveryServer.Services[i].HTML.FullTemplatePath = c.HTML.FullTemplatePath 156 | } else { 157 | // If it was redefined we need to update the TemplatePath, first lets 158 | // verify that the template directory is found on the file system, 159 | // if it is then copy all of the settings for the template path and 160 | // then update the actual value. 161 | text := "discoveryserver.services[" + indexString + "].html.templatedir" 162 | problemsFound += c.verifyHTMLTemplateDir(text, s.HTML.TemplateDir) 163 | c.DiscoveryServer.Services[i].HTML.FullTemplatePath = c.HTML.FullTemplatePath 164 | c.DiscoveryServer.Services[i].HTML.FullTemplatePath = c.Global.Prefix + s.HTML.TemplateDir.Value 165 | } 166 | 167 | if s.HTML.TemplateFiles.Discovery.Set == false || s.HTML.TemplateFiles.Discovery.Valid == false { 168 | c.DiscoveryServer.Services[i].HTML.TemplateFiles.Discovery = c.HTML.TemplateFiles.Discovery 169 | } else { 170 | // If it was redefined we need to verify that it is found on the file system. 171 | text := "discoveryserver.services[" + indexString + "].html.templatefiles.discovery" 172 | problemsFound += c.verifyHTMLTemplateFile(text, s.HTML.FullTemplatePath, s.HTML.TemplateFiles.Discovery) 173 | } 174 | } // End for loop 175 | 176 | // ---------------------------------------------------------------------- 177 | // Return number of errors if there are any 178 | // ---------------------------------------------------------------------- 179 | if problemsFound > 0 { 180 | c.Logger.Println("ERROR: The Discovery HTML configuration has", problemsFound, "error(s)") 181 | } 182 | return problemsFound 183 | } 184 | 185 | // ---------------------------------------- 186 | // Verify API Root HTML Files 187 | // ---------------------------------------- 188 | 189 | // verifyAPIRootHTMLConfig - This method will check each of the defined HTML template files 190 | // and make sure they exist. It will also check to see if any of them have been redefined at 191 | // a service level. If they have, it will check to see if those exists as well. 192 | // This method will only be called from VerifyServerConfig() if 193 | // APIRootServer.HTMLEnabled == true 194 | func (c *ServerConfig) verifyAPIRootHTMLConfig() int { 195 | var problemsFound = 0 196 | 197 | // If HTML output is not enabled globally, then skip these tests 198 | if c.HTML.Enabled.Value == false { 199 | return problemsFound 200 | } 201 | 202 | // Check to see if any of the HTML configurations were redefined at each service level 203 | for i, s := range c.APIRootServer.Services { 204 | indexString := strconv.Itoa(i) 205 | 206 | // Check to see if the following values were redefined and valid. If 207 | // they were not redefined or they are invalid (set to "null" and thus 208 | // invalid) then lets set them to the same as the global configuration. 209 | // Copy all of the settings for the object, not just the value. 210 | // If the value was redefined and is valid, lets just leave it alone. 211 | if s.HTML.Enabled.Set == false || s.HTML.Enabled.Valid == false { 212 | c.APIRootServer.Services[i].HTML.Enabled = c.HTML.Enabled 213 | } 214 | 215 | if s.HTML.TemplateDir.Set == false || s.HTML.TemplateDir.Valid == false { 216 | c.APIRootServer.Services[i].HTML.TemplateDir = c.HTML.TemplateDir 217 | c.APIRootServer.Services[i].HTML.FullTemplatePath = c.HTML.FullTemplatePath 218 | } else { 219 | // If it was redefined we need to update the TemplatePath, first lets 220 | // verify that the template directory is found on the file system, 221 | // if it is then copy all of the settings for the template path and 222 | // then update the actual value. 223 | text := "apirootserver.services[" + indexString + "].html.templatedir" 224 | problemsFound += c.verifyHTMLTemplateDir(text, s.HTML.TemplateDir) 225 | c.APIRootServer.Services[i].HTML.FullTemplatePath = c.HTML.FullTemplatePath 226 | c.APIRootServer.Services[i].HTML.FullTemplatePath = c.Global.Prefix + s.HTML.TemplateDir.Value 227 | } 228 | 229 | if s.HTML.TemplateFiles.APIRoot.Set == false || s.HTML.TemplateFiles.APIRoot.Valid == false { 230 | c.APIRootServer.Services[i].HTML.TemplateFiles.APIRoot = c.HTML.TemplateFiles.APIRoot 231 | } else { 232 | // If it was redefined we need to verify that it is found on the file system. 233 | text := "apirootserver.services[" + indexString + "].html.templatefiles.apiroot" 234 | problemsFound += c.verifyHTMLTemplateFile(text, s.HTML.FullTemplatePath, s.HTML.TemplateFiles.APIRoot) 235 | } 236 | 237 | if s.HTML.TemplateFiles.Collections.Set == false || s.HTML.TemplateFiles.Collections.Valid == false { 238 | c.APIRootServer.Services[i].HTML.TemplateFiles.Collections = c.HTML.TemplateFiles.Collections 239 | } else { 240 | // If it was redefined we need to verify that it is found on the file system. 241 | text := "apirootserver.services[" + indexString + "].html.templatefiles.collections" 242 | problemsFound += c.verifyHTMLTemplateFile(text, s.HTML.FullTemplatePath, s.HTML.TemplateFiles.Collections) 243 | } 244 | 245 | if s.HTML.TemplateFiles.Collection.Set == false || s.HTML.TemplateFiles.Collection.Valid == false { 246 | c.APIRootServer.Services[i].HTML.TemplateFiles.Collection = c.HTML.TemplateFiles.Collection 247 | } else { 248 | // If it was redefined we need to verify that it is found on the file system. 249 | text := "apirootserver.services[" + indexString + "].html.templatefiles.collection" 250 | problemsFound += c.verifyHTMLTemplateFile(text, s.HTML.FullTemplatePath, s.HTML.TemplateFiles.Collection) 251 | } 252 | 253 | if s.HTML.TemplateFiles.Objects.Set == false || s.HTML.TemplateFiles.Objects.Valid == false { 254 | c.APIRootServer.Services[i].HTML.TemplateFiles.Objects = c.HTML.TemplateFiles.Objects 255 | } else { 256 | // If it was redefined we need to verify that it is found on the file system. 257 | text := "apirootserver.services[" + indexString + "].html.templatefiles.objects" 258 | problemsFound += c.verifyHTMLTemplateFile(text, s.HTML.FullTemplatePath, s.HTML.TemplateFiles.Objects) 259 | } 260 | 261 | if s.HTML.TemplateFiles.Versions.Set == false || s.HTML.TemplateFiles.Versions.Valid == false { 262 | c.APIRootServer.Services[i].HTML.TemplateFiles.Versions = c.HTML.TemplateFiles.Versions 263 | } else { 264 | // If it was redefined we need to verify that it is found on the file system. 265 | text := "apirootserver.services[" + indexString + "].html.templatefiles.versions" 266 | problemsFound += c.verifyHTMLTemplateFile(text, s.HTML.FullTemplatePath, s.HTML.TemplateFiles.Versions) 267 | } 268 | 269 | if s.HTML.TemplateFiles.Manifest.Set == false || s.HTML.TemplateFiles.Manifest.Valid == false { 270 | c.APIRootServer.Services[i].HTML.TemplateFiles.Manifest = c.HTML.TemplateFiles.Manifest 271 | } else { 272 | // If it was redefined we need to verify that it is found on the file system. 273 | text := "apirootserver.services[" + indexString + "].html.templatefiles.manifest" 274 | problemsFound += c.verifyHTMLTemplateFile(text, s.HTML.FullTemplatePath, s.HTML.TemplateFiles.Manifest) 275 | } 276 | } // End for loop 277 | 278 | // ---------------------------------------------------------------------- 279 | // Return number of errors if there are any 280 | // ---------------------------------------------------------------------- 281 | if problemsFound > 0 { 282 | c.Logger.Println("ERROR: The API Root HTML configuration has", problemsFound, "error(s)") 283 | } 284 | return problemsFound 285 | } 286 | -------------------------------------------------------------------------------- /internal/config/verifyServicesConfig.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015-2018 Bret Jordan, All rights reserved. 2 | // 3 | // Use of this source code is governed by an Apache 2.0 license 4 | // that can be found in the LICENSE file in the root of the source tree. 5 | 6 | package config 7 | 8 | import ( 9 | "strings" 10 | ) 11 | 12 | /* 13 | verifyDisocveryConfig - This method will verify all of the configuration 14 | directives for the TAXII Discovery Service 15 | */ 16 | func (c *ServerConfig) verifyDiscoveryConfig() int { 17 | var problemsFound = 0 18 | 19 | // This variable will track if any of the actual discovery services are 20 | // enabled. If the outer service says yes, but no actual services are 21 | // enabled, throw an error. 22 | var isServiceEnabled = false 23 | 24 | // Loop through each Discovery Service and verify its configuration 25 | for _, value := range c.DiscoveryServer.Services { 26 | 27 | // Check to see if this service instance is enabled. 28 | if value.Enabled == true { 29 | isServiceEnabled = true 30 | } 31 | 32 | // Verify the Discovery Path is defined in the configuration file and set 33 | // the "full path" value 34 | if value.Path == "" { 35 | c.Logger.Println("CONFIG: One or more Discovery Services is missing the 'path' directive in the configuration file") 36 | problemsFound++ 37 | } else { 38 | if !strings.HasSuffix(value.Path, "/") { 39 | c.Logger.Println("CONFIG: The path in one or more Discovery Services is missing the ending slash '/'") 40 | problemsFound++ 41 | } 42 | if !strings.HasPrefix(value.Path, "/") { 43 | c.Logger.Println("CONFIG: The path in one or more Discovery Services is missing the starting slash '/'") 44 | problemsFound++ 45 | } 46 | } 47 | 48 | // Verify the Discovery Resource that is referenced actually exists 49 | if _, ok := c.DiscoveryResources[value.ResourceID]; !ok { 50 | msg := "CONFIG: The Discovery Resource " + value.ResourceID + " is missing from the configuration file" 51 | c.Logger.Println(msg) 52 | problemsFound++ 53 | } 54 | } // end for loop 55 | 56 | // Log an error if there are no Discovery services actually enabled. 57 | if isServiceEnabled == false { 58 | c.Logger.Println("CONFIG: While the Discovery Server is enabled, there are no Discovery Services that are enabled") 59 | problemsFound++ 60 | } 61 | 62 | // Return errors if there were any 63 | if problemsFound > 0 { 64 | c.Logger.Println("ERROR: The Discovery configuration has", problemsFound, "error(s)") 65 | } 66 | return problemsFound 67 | } 68 | 69 | /* 70 | verifyAPIRootConfig - This method will verify all of the configuration 71 | directives for the TAXII API Root Service 72 | */ 73 | func (c *ServerConfig) verifyAPIRootConfig() int { 74 | var problemsFound = 0 75 | 76 | // This variable will track if any of the actual api root services are 77 | // enabled. If the outer service says yes, but no actual services are 78 | // enabled, throw an error. 79 | var isServiceEnabled = false 80 | 81 | // API Service Directives 82 | for _, value := range c.APIRootServer.Services { 83 | 84 | // If this service instance is enabled 85 | if value.Enabled == true { 86 | isServiceEnabled = true 87 | } 88 | 89 | // Verify the API Path is correctly defined in the configuration file. 90 | // Example: "path": "/api1/" 91 | if value.Path == "" { 92 | c.Logger.Println("CONFIG: One or more API Root Services is missing the 'path' directive in the configuration file") 93 | problemsFound++ 94 | } else { 95 | if !strings.HasSuffix(value.Path, "/") { 96 | c.Logger.Println("CONFIG: The path in one or more API Roots is missing the ending slash '/'") 97 | problemsFound++ 98 | } 99 | if !strings.HasPrefix(value.Path, "/") { 100 | c.Logger.Println("CONFIG: The path in one or more API Roots is missing the starting slash '/'") 101 | problemsFound++ 102 | } 103 | } 104 | 105 | // Verify the API Resource is found 106 | if _, ok := c.APIRootResources[value.ResourceID]; !ok { 107 | value := "CONFIG: The API Root Resource " + value.ResourceID + " is missing from the configuration file" 108 | c.Logger.Println(value) 109 | problemsFound++ 110 | } 111 | 112 | // Verify the Collection Resources are found 113 | if value.Collections.Enabled == true { 114 | for _, col := range value.Collections.ReadAccess { 115 | if _, ok := c.CollectionResources[col]; !ok { 116 | value := "CONFIG: One or more API Roots is using a read access collection of " + col + " that is missing from the configuration file" 117 | c.Logger.Println(value) 118 | problemsFound++ 119 | } 120 | } 121 | 122 | for _, col := range value.Collections.WriteAccess { 123 | if _, ok := c.CollectionResources[col]; !ok { 124 | value := "CONFIG: One or more API Roots is using a read access collection of " + col + " that is missing from the configuration file" 125 | c.Logger.Println(value) 126 | problemsFound++ 127 | } 128 | } 129 | } 130 | 131 | } // End for loop on services 132 | 133 | // Log an error if there are no API Root services actually enabled. 134 | if isServiceEnabled == false { 135 | c.Logger.Println("CONFIG: While the API Root Server is enabled, there are no API Root Services that are enabled") 136 | problemsFound++ 137 | } 138 | 139 | // Return errors if there were any 140 | if problemsFound > 0 { 141 | c.Logger.Println("ERROR: The API Root configuration has", problemsFound, "error(s)") 142 | } 143 | return problemsFound 144 | } 145 | -------------------------------------------------------------------------------- /internal/handlers/auth.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015-2018 Bret Jordan, All rights reserved. 2 | // 3 | // Use of this source code is governed by an Apache 2.0 license 4 | // that can be found in the LICENSE file in the root of the source tree. 5 | 6 | package handlers 7 | 8 | /* 9 | authenticate - This method will perform an authentication check to see if the 10 | supplied credentials are valid. 11 | */ 12 | func (s *ServerHandler) authenticate(username, password string, valid bool) bool { 13 | 14 | // If the user did not supply any credentials, then they failed authentication 15 | if valid == false { 16 | return false 17 | } 18 | 19 | if username == "taxii" && password == "password" { 20 | return true 21 | } 22 | return false 23 | } 24 | -------------------------------------------------------------------------------- /internal/handlers/contentHandler.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015-2018 Bret Jordan, All rights reserved. 2 | // 3 | // Use of this source code is governed by an Apache 2.0 license 4 | // that can be found in the LICENSE file in the root of the source tree. 5 | 6 | package handlers 7 | 8 | import ( 9 | "encoding/json" 10 | "html/template" 11 | "net/http" 12 | "path" 13 | 14 | "github.com/freetaxii/libstix2/defs" 15 | "github.com/freetaxii/libstix2/objects" 16 | "github.com/freetaxii/libstix2/resources/collections" 17 | "github.com/freetaxii/libstix2/resources/envelope" 18 | "github.com/freetaxii/libstix2/resources/status" 19 | "github.com/freetaxii/libstix2/stixid" 20 | "github.com/freetaxii/server/internal/headers" 21 | "github.com/gorilla/mux" 22 | ) 23 | 24 | /* 25 | STIXContentServerHandler - This method will handle all of the requests for STIX 26 | objects from the TAXII server. 27 | */ 28 | func (s *ServerHandler) STIXContentServerHandler(w http.ResponseWriter, r *http.Request) { 29 | var addedFirst, addedLast string 30 | 31 | s.Logger.Infoln("INFO: Found GET Request from", r.RemoteAddr, "for collection:", s.CollectionID) 32 | 33 | // If trace is enabled in the logger, than decode the HTTP Request to the log 34 | if s.Logger.GetLevel("trace") { 35 | headers.DebugHttpRequest(r) 36 | } 37 | 38 | // -------------------------------------------------- 39 | // 1st Check Authentication 40 | // -------------------------------------------------- 41 | // If authentication is required and the client does not provide credentials 42 | // or their credentials do not match, then send an error message. 43 | // We need to return right here as to prevent further processing. 44 | if s.Authenticated == true { 45 | s.Logger.Debugln("DEBUG: Authentication Enabled") 46 | if s.BasicAuth == true { 47 | s.Logger.Debugln("DEBUG: Basic Authentication Enabled") 48 | w.Header().Set("WWW-Authenticate", `Basic realm="Authentication Required"`) 49 | if success := s.authenticate(r.BasicAuth()); success != true { 50 | s.Logger.Debugln("DEBUG: Authentication failed for", r.RemoteAddr, "at", r.RequestURI) 51 | s.sendUnauthenticatedError(w) 52 | return 53 | } 54 | } else { 55 | // If authentication is enabled, but basic is not, then fail since 56 | // no other authentication is currently enabled. 57 | s.Logger.Debugln("DEBUG: Authentication method from", r.RemoteAddr, "at", r.RequestURI, "not supported") 58 | s.sendUnauthenticatedError(w) 59 | return 60 | } 61 | } // End Authentication Check 62 | 63 | // ---------------------------------------------------------------------- 64 | // Handle URL Parameters and Path Variables 65 | // ---------------------------------------------------------------------- 66 | 67 | // Setup Query object to handle URL parameters and path variables 68 | q := collections.NewCollectionQuery(s.CollectionID, s.ServerRecordLimit) 69 | 70 | urlParameters := r.URL.Query() 71 | s.Logger.Debugln("DEBUG: Client", r.RemoteAddr, "sent the following (", len(urlParameters), ") url parameters:", urlParameters) 72 | 73 | errURLParameters := s.processURLParameters(q, urlParameters) 74 | if errURLParameters != nil { 75 | s.Logger.Warnln("WARN: invalid URL parameters from client", r.RemoteAddr, "with URL parameters", urlParameters, errURLParameters) 76 | } 77 | 78 | urlvars := mux.Vars(r) 79 | 80 | // ---------------------------------------------------------------------- 81 | // Handle Requests for Manifest data 82 | // ---------------------------------------------------------------------- 83 | if path.Base(r.URL.Path) == "manifest" { 84 | s.Logger.Debugln("DEBUG: Found a GET Request for manifests") 85 | results, err := s.DS.GetManifestData(*q) 86 | 87 | if err != nil { 88 | s.Logger.Infoln("INFO: Sending error response to", r.RemoteAddr, "due to:", err.Error()) 89 | s.sendGetObjectsError(w) 90 | return 91 | } 92 | s.Resource = results.ManifestData 93 | addedFirst = results.DateAddedFirst 94 | addedLast = results.DateAddedLast 95 | s.Logger.Infoln("INFO: Sending response to", r.RemoteAddr) 96 | 97 | } 98 | 99 | // ---------------------------------------------------------------------- 100 | // Handle Requests for all Objects 101 | // ---------------------------------------------------------------------- 102 | if path.Base(r.URL.Path) == "objects" { 103 | s.Logger.Debugln("DEBUG: Found a GET Request for all objects") 104 | results, err := s.DS.GetObjects(*q) 105 | 106 | if err != nil { 107 | s.Logger.Infoln("INFO: Sending error response to", r.RemoteAddr, "due to:", err.Error()) 108 | s.sendGetObjectsError(w) 109 | return 110 | } 111 | s.Resource = results.ObjectData 112 | addedFirst = results.DateAddedFirst 113 | addedLast = results.DateAddedLast 114 | s.Logger.Infoln("INFO: Sending response to", r.RemoteAddr) 115 | 116 | } 117 | 118 | // ---------------------------------------------------------------------- 119 | // Handle Requests for an Object by ID and Versions 120 | // ---------------------------------------------------------------------- 121 | if urlvars["objectid"] != "" { 122 | urlObjectID := urlvars["objectid"] 123 | s.Logger.Debugln("DEBUG: Client", r.RemoteAddr, "sent URL path value:", urlObjectID) 124 | 125 | // Since this endpoint does not allow the match[id] or match[type] filters 126 | // lets clear them out, just in case the client is behaving badly 127 | if len(q.STIXID) > 0 { 128 | s.Logger.Infoln("INFO: Client", r.RemoteAddr, "sent a STIX ID as a filter parameter when not allowed") 129 | q.STIXID = nil 130 | } 131 | 132 | if len(q.STIXType) > 0 { 133 | s.Logger.Infoln("INFO: Client", r.RemoteAddr, "sent a STIX Type as a filter parameter when not allowed") 134 | q.STIXType = nil 135 | } 136 | 137 | // TODO check to see if objectid is a valid STIX ID before we add it to the 138 | // filter list. 139 | // TODO change to make work with custom objects 140 | if stixid.ValidSTIXID(urlObjectID) { 141 | q.STIXID = append(q.STIXID, urlObjectID) 142 | } else { 143 | s.Logger.Infoln("INFO: Sending error response to", r.RemoteAddr, "due to invalid STIX ID in object by ID path") 144 | s.sendGetObjectsError(w) 145 | return 146 | } 147 | 148 | if path.Base(r.URL.Path) == "versions" { 149 | // This is a simple get versions of an object ID request 150 | s.Logger.Debugln("DEBUG: Found a GET Request for the versions of an object by ID") 151 | 152 | // Since this endpoint does not allow the match[id] or match[type] filters 153 | // lets clear them out, just in case the client is behaving badly 154 | if len(q.STIXVersion) > 0 { 155 | s.Logger.Infoln("INFO: Client", r.RemoteAddr, "sent a STIX Version as a filter parameter when not allowed") 156 | q.STIXVersion = nil 157 | } 158 | 159 | results, err := s.DS.GetVersions(*q) 160 | 161 | if err != nil { 162 | s.Logger.Infoln("INFO: Sending error response to", r.RemoteAddr, "due to:", err.Error()) 163 | s.sendGetObjectsError(w) 164 | return 165 | } 166 | s.Resource = results.VersionsData 167 | addedFirst = results.DateAddedFirst 168 | addedLast = results.DateAddedLast 169 | s.Logger.Infoln("INFO: Sending response to", r.RemoteAddr) 170 | 171 | } else { 172 | // This is a simple get objects by ID request 173 | s.Logger.Debugln("DEBUG: Found a GET Request for an object by ID") 174 | results, err := s.DS.GetObjects(*q) 175 | 176 | if err != nil { 177 | s.Logger.Infoln("INFO: Sending error response to", r.RemoteAddr, "due to:", err.Error()) 178 | s.sendGetObjectsError(w) 179 | return 180 | } 181 | s.Resource = results.ObjectData 182 | addedFirst = results.DateAddedFirst 183 | addedLast = results.DateAddedLast 184 | s.Logger.Infoln("INFO: Sending response to", r.RemoteAddr) 185 | 186 | } 187 | } 188 | 189 | // -------------------------------------------------- 190 | // Encode outgoing response message 191 | // -------------------------------------------------- 192 | 193 | // Get Accept Header 194 | var acceptHeader headers.MediaType 195 | acceptHeader.ParseTAXII(r.Header.Get("Accept")) 196 | 197 | // Set header for TLS 198 | w.Header().Add("Strict-Transport-Security", "max-age=86400; includeSubDomains") 199 | w.Header().Add("X-TAXII-Date-Added-First", addedFirst) 200 | w.Header().Add("X-TAXII-Date-Added-Last", addedLast) 201 | 202 | // This clearly does not work yet. Need to move the declaration up and 203 | // do a check to see if there is data coming back from the query 204 | var objectNotFound = false 205 | if objectNotFound == true { 206 | s.sendStatusNotFound(w) 207 | return 208 | } 209 | 210 | if acceptHeader.TAXII21 == true { 211 | // Setup JSON stream encoder 212 | j := json.NewEncoder(w) 213 | w.Header().Set("Content-Type", defs.MEDIA_TYPE_TAXII21) 214 | w.WriteHeader(http.StatusOK) 215 | j.Encode(s.Resource) 216 | 217 | } else if acceptHeader.JSON == true { 218 | // Setup JSON stream encoder 219 | j := json.NewEncoder(w) 220 | w.Header().Set("Content-Type", defs.MEDIA_TYPE_JSON) 221 | w.WriteHeader(http.StatusOK) 222 | j.SetIndent("", " ") 223 | j.Encode(s.Resource) 224 | 225 | } else if s.HTMLEnabled == true && acceptHeader.HTML == true { 226 | w.Header().Set("Content-Type", defs.MEDIA_TYPE_HTML) 227 | w.WriteHeader(http.StatusOK) 228 | // I needed to convert this to actual JSON since if I just used 229 | // s.Resource like in other handlers I would get the string output of 230 | // a Golang struct which is not the same. The reason it works else where 231 | // is I am not printing the whole object, but rather, referencing the 232 | // parts as I need them. 233 | jsondata, err := json.MarshalIndent(s.Resource, "", " ") 234 | if err != nil { 235 | s.Logger.Fatal("Unable to create JSON Message") 236 | } 237 | s.Resource = string(jsondata) 238 | 239 | // ---------------------------------------------------------------------- 240 | // Setup HTML Template 241 | // ---------------------------------------------------------------------- 242 | htmlTemplateResource := template.Must(template.ParseFiles(s.HTMLTemplate)) 243 | htmlTemplateResource.Execute(w, s) 244 | 245 | } else { 246 | s.sendNotAcceptableError(w) 247 | return 248 | } 249 | } 250 | 251 | /* 252 | ObjectsServerWriteHandler - This method will handle all POST requests of STIX 253 | objects from the TAXII server. 254 | */ 255 | func (s *ServerHandler) ObjectsServerWriteHandler(w http.ResponseWriter, r *http.Request) { 256 | 257 | s.Logger.Infoln("INFO: Found POST Request on the Objects Server from", r.RemoteAddr, "for collection:", s.CollectionID) 258 | 259 | // If trace is enabled in the logger, than decode the HTTP Request to the log 260 | if s.Logger.GetLevel("trace") { 261 | headers.DebugHttpRequest(r) 262 | } 263 | 264 | // -------------------------------------------------- 265 | // 1st Check Authentication 266 | // -------------------------------------------------- 267 | // If authentication is required and the client does not provide credentials 268 | // or their credentials do not match, then send an error message. 269 | // We need to return right here as to prevent further processing. 270 | if s.Authenticated == true { 271 | s.Logger.Debugln("DEBUG: Authentication Enabled") 272 | if s.BasicAuth == true { 273 | s.Logger.Debugln("DEBUG: Basic Authentication Enabled") 274 | w.Header().Set("WWW-Authenticate", `Basic realm="Authentication Required"`) 275 | if success := s.authenticate(r.BasicAuth()); success != true { 276 | s.Logger.Debugln("DEBUG: Authentication failed for", r.RemoteAddr, "at", r.RequestURI) 277 | s.sendUnauthenticatedError(w) 278 | return 279 | } 280 | } else { 281 | // If authentication is enabled, but basic is not, then fail since 282 | // no other authentication is currently enabled. 283 | s.Logger.Debugln("DEBUG: Authentication method from", r.RemoteAddr, "at", r.RequestURI, "not supported") 284 | s.sendUnauthenticatedError(w) 285 | return 286 | } 287 | } // End Authentication Check 288 | 289 | // ---------------------------------------------------------------------- 290 | // Check content-type header first 291 | // ---------------------------------------------------------------------- 292 | var contentHeader headers.MediaType 293 | contentHeader.ParseTAXII(r.Header.Get("Content-type")) 294 | 295 | if contentHeader.TAXII21 != true { 296 | s.sendUnsupportedMediaTypeError(w) 297 | return 298 | } 299 | 300 | statusMessage := status.New() 301 | statusMessage.SetNewID() 302 | statusMessage.SetStatusCompleted() 303 | statusMessage.SetRequestTimestampToCurrentTime() 304 | 305 | // ---------------------------------------------------------------------- 306 | // Decode the envelope object itself, but leave the objects array as an 307 | // array of raw JSON object objects, we will decode each one later. 308 | // ---------------------------------------------------------------------- 309 | e, err := envelope.DecodeRaw(r.Body) 310 | if err != nil { 311 | s.Logger.Errorln("ERROR: Could not decode provided envelope") 312 | 313 | s.sendParseObjectsError(w) 314 | return 315 | } 316 | 317 | // ---------------------------------------------------------------------- 318 | // Decode each object in the envelope one at a time. If the object is valid 319 | // write it off to the datastore. 320 | // Lets keep a count of the number of objects that are successful and the 321 | // number that are not successful in addition to a total count 322 | // ---------------------------------------------------------------------- 323 | totalCount := 0 324 | successCount := 0 325 | failureCount := 0 326 | for _, v := range e.Objects { 327 | totalCount++ 328 | s.Logger.Debugln("DEBUG: Processing envelope object number", totalCount) 329 | 330 | // First, decode the first object from the envelope if it succeeds try to 331 | // add it to the datastore 332 | o, err := objects.Decode(v) 333 | id := o.GetID() 334 | if err != nil { 335 | // TODO Track something to send error back to client in status resource 336 | s.Logger.Errorln("ERROR: Error decoding object in envelope", err) 337 | failureCount++ 338 | statusMessage.CreateFailureDetails(id, "", "Object failed") 339 | // If there is an error, lets just skip and move on to the next object 340 | continue 341 | } 342 | 343 | // Add the object to the datastore, if the decode was successful 344 | s.Logger.Debugln("DEBUG: Adding object", id, "to the datastore") 345 | err = s.DS.AddObject(o) 346 | if err != nil { 347 | s.Logger.Errorln("ERROR: Error adding object", id, "to datastore", err) 348 | failureCount++ 349 | statusMessage.CreateFailureDetails(id, "", "Object failed") 350 | // If there was an error, lets just skip and move on to the next object 351 | continue 352 | } 353 | successCount++ 354 | statusMessage.CreateSuccessDetails(id, "", "Object added") 355 | 356 | // If the add was successful then lets add an entry in to the collection 357 | // record table. 358 | s.Logger.Debugln("DEBUG: Adding Collection Entry of", s.CollectionID, id) 359 | err = s.DS.AddToCollection(s.CollectionID, id) 360 | if err != nil { 361 | s.Logger.Debugln(err) 362 | } 363 | } 364 | 365 | statusMessage.SetTotalCount(totalCount) 366 | statusMessage.SetSuccessCount(successCount) 367 | statusMessage.SetFailureCount(failureCount) 368 | 369 | s.Resource = statusMessage 370 | 371 | s.Logger.Debugln("DEBUG: Total number of objects in Envelope", totalCount) 372 | s.Logger.Debugln("DEBUG: Total objects successfully added to datastore", successCount) 373 | s.Logger.Debugln("DEBUG: Total objects that failed to be added to the datastore", failureCount) 374 | 375 | s.Logger.Infoln("INFO: Sending response to", r.RemoteAddr) 376 | 377 | // -------------------------------------------------- 378 | // Encode outgoing response message 379 | // -------------------------------------------------- 380 | 381 | // Get Accept Header 382 | var acceptHeader headers.MediaType 383 | acceptHeader.ParseTAXII(r.Header.Get("Accept")) 384 | 385 | // Set header for TLS 386 | w.Header().Add("Strict-Transport-Security", "max-age=86400; includeSubDomains") 387 | 388 | if acceptHeader.TAXII21 == true { 389 | // Setup JSON stream encoder for response 390 | j := json.NewEncoder(w) 391 | w.Header().Set("Content-Type", defs.MEDIA_TYPE_TAXII21) 392 | w.WriteHeader(http.StatusAccepted) 393 | j.Encode(s.Resource) 394 | 395 | } else if acceptHeader.JSON == true { 396 | // Setup JSON stream encoder for response 397 | j := json.NewEncoder(w) 398 | w.Header().Set("Content-Type", defs.MEDIA_TYPE_JSON) 399 | w.WriteHeader(http.StatusAccepted) 400 | j.SetIndent("", " ") 401 | j.Encode(s.Resource) 402 | 403 | } else if s.HTMLEnabled == true && acceptHeader.HTML == true { 404 | w.Header().Set("Content-Type", defs.MEDIA_TYPE_HTML) 405 | w.WriteHeader(http.StatusAccepted) 406 | 407 | // I needed to convert this to actual JSON since if I just used 408 | // s.Resource like in other handlers I would get the string output of 409 | // a Golang struct which is not the same. The reason it works else where 410 | // is I am not printing the whole object, but rather, referencing the 411 | // parts as I need them. 412 | jsondata, err := json.MarshalIndent(s.Resource, "", " ") 413 | if err != nil { 414 | s.Logger.Fatal("Unable to create JSON Message") 415 | } 416 | s.Resource = string(jsondata) 417 | 418 | // ---------------------------------------------------------------------- 419 | // Setup HTML Template 420 | // ---------------------------------------------------------------------- 421 | htmlTemplateResource := template.Must(template.ParseFiles(s.HTMLTemplate)) 422 | htmlTemplateResource.Execute(w, s) 423 | 424 | } else { 425 | s.sendNotAcceptableError(w) 426 | return 427 | } 428 | } 429 | -------------------------------------------------------------------------------- /internal/handlers/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015-2018 Bret Jordan, All rights reserved. 2 | // 3 | // Use of this source code is governed by an Apache 2.0 license 4 | // that can be found in the LICENSE file in the root of the source tree. 5 | 6 | package handlers 7 | -------------------------------------------------------------------------------- /internal/handlers/errors.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015-2018 Bret Jordan, All rights reserved. 2 | // 3 | // Use of this source code is governed by an Apache 2.0 license 4 | // that can be found in the LICENSE file in the root of the source tree. 5 | 6 | package handlers 7 | 8 | import ( 9 | "encoding/json" 10 | "net/http" 11 | 12 | "github.com/freetaxii/libstix2/defs" 13 | "github.com/freetaxii/libstix2/resources/taxiierror" 14 | ) 15 | 16 | /* 17 | sendUnauthenticatedError - This method will send the correct TAXII error message 18 | for a session that is unauthenticated. 19 | */ 20 | func (s *ServerHandler) sendUnauthenticatedError(w http.ResponseWriter) { 21 | 22 | // Setup JSON stream encoder 23 | j := json.NewEncoder(w) 24 | 25 | w.Header().Set("Content-Type", defs.MEDIA_TYPE_TAXII21) 26 | w.WriteHeader(http.StatusUnauthorized) 27 | 28 | e := taxiierror.New() 29 | e.SetTitle("Authentication Required") 30 | e.SetDescription("The requested resources requires authentication.") 31 | e.SetErrorCode("401") 32 | e.SetHTTPStatus("401 Unauthorized") 33 | 34 | j.SetIndent("", " ") 35 | j.Encode(e) 36 | } 37 | 38 | /* 39 | sendNotAcceptableError - This method will send the correct TAXII error 40 | message for a session that requests an unsupported media type in the accept 41 | header. 42 | */ 43 | func (s *ServerHandler) sendNotAcceptableError(w http.ResponseWriter) { 44 | 45 | // Setup JSON stream encoder 46 | j := json.NewEncoder(w) 47 | 48 | w.Header().Set("Content-Type", defs.MEDIA_TYPE_TAXII21) 49 | w.WriteHeader(http.StatusNotAcceptable) 50 | 51 | e := taxiierror.New() 52 | e.SetTitle("Wrong Media Type") 53 | e.SetDescription("The requested media type in the accept header is not supported.") 54 | e.SetErrorCode("406") 55 | e.SetHTTPStatus("406 Not Acceptable") 56 | 57 | j.SetIndent("", " ") 58 | j.Encode(e) 59 | } 60 | 61 | /* 62 | sendUnsupportedMediaTypeError - This method will send the correct TAXII error 63 | message for a session that requests an unsupported media type in the content-type 64 | header. 65 | */ 66 | func (s *ServerHandler) sendUnsupportedMediaTypeError(w http.ResponseWriter) { 67 | 68 | // Setup JSON stream encoder 69 | j := json.NewEncoder(w) 70 | 71 | w.Header().Set("Content-Type", defs.MEDIA_TYPE_TAXII21) 72 | w.WriteHeader(http.StatusUnsupportedMediaType) 73 | 74 | e := taxiierror.New() 75 | e.SetTitle("Wrong Media Type") 76 | e.SetDescription("The requested media type in the content-type header is not supported.") 77 | e.SetErrorCode("415") 78 | e.SetHTTPStatus("415 Unsupported Media Type") 79 | 80 | j.SetIndent("", " ") 81 | j.Encode(e) 82 | } 83 | 84 | /* 85 | sendGetObjectsError - This method will send the correct TAXII error 86 | message for a session that requests some objects but an error is returned. 87 | */ 88 | func (s *ServerHandler) sendGetObjectsError(w http.ResponseWriter) { 89 | 90 | // Setup JSON stream encoder 91 | j := json.NewEncoder(w) 92 | 93 | w.Header().Set("Content-Type", defs.MEDIA_TYPE_TAXII21) 94 | w.WriteHeader(http.StatusNotFound) 95 | 96 | e := taxiierror.New() 97 | e.SetTitle("Get Objects Error") 98 | e.SetDescription("The request for objects caused an error.") 99 | e.SetErrorCode("404") 100 | e.SetHTTPStatus("404 Not Found") 101 | 102 | j.SetIndent("", " ") 103 | j.Encode(e) 104 | } 105 | 106 | /* 107 | sendParseObjectsError - This method will send the correct TAXII error 108 | message for a session that posts some objects but an error is returned. 109 | */ 110 | func (s *ServerHandler) sendParseObjectsError(w http.ResponseWriter) { 111 | 112 | // Setup JSON stream encoder 113 | j := json.NewEncoder(w) 114 | 115 | w.Header().Set("Content-Type", defs.MEDIA_TYPE_TAXII21) 116 | w.WriteHeader(http.StatusBadRequest) 117 | 118 | e := taxiierror.New() 119 | e.SetTitle("Post Objects Error") 120 | e.SetDescription("The request to post objects caused an error.") 121 | e.SetErrorCode("400") 122 | e.SetHTTPStatus("400 Bad Request") 123 | 124 | j.SetIndent("", " ") 125 | j.Encode(e) 126 | } 127 | 128 | /* 129 | sendStatusNotFound - This method will send the correct TAXII error 130 | message for a session that requests some objects but no records were found. 131 | */ 132 | func (s *ServerHandler) sendStatusNotFound(w http.ResponseWriter) { 133 | 134 | // Setup JSON stream encoder 135 | j := json.NewEncoder(w) 136 | 137 | w.Header().Set("Content-Type", defs.MEDIA_TYPE_TAXII21) 138 | w.WriteHeader(http.StatusNotFound) 139 | 140 | e := taxiierror.New() 141 | e.SetTitle("No Objects Found") 142 | e.SetDescription("There were no objects returned matching the request.") 143 | e.SetErrorCode("404") 144 | e.SetHTTPStatus("404 Not Found") 145 | 146 | j.SetIndent("", " ") 147 | j.Encode(e) 148 | } 149 | -------------------------------------------------------------------------------- /internal/handlers/getObject_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015-2018 Bret Jordan, All rights reserved. 2 | // 3 | // Use of this source code is governed by an Apache 2.0 license 4 | // that can be found in the LICENSE file in the root of the source tree. 5 | 6 | package handlers 7 | 8 | import ( 9 | "github.com/freetaxii/libstix2/datastore" 10 | "github.com/freetaxii/libstix2/objects" 11 | //"github.com/freetaxii/libstix2/resources" 12 | "net/http" 13 | "net/http/httptest" 14 | "testing" 15 | ) 16 | 17 | type dummydb struct { 18 | datastore.Datastorer 19 | } 20 | 21 | func (db *dummydb) GetObject(stixid string) (interface{}, error) { 22 | return objects.NewIndicator("2.0"), nil 23 | } 24 | func (db *dummydb) GetObjectsFromCollection(collectionid string) objects.BundleType { 25 | b := objects.NewBundle() 26 | b.SetID("bundle--1234") 27 | return b 28 | } 29 | 30 | func TestHealthCheckHandler(t *testing.T) { 31 | // Create a request to pass to our handler. We don't have any query parameters for now, so we'll 32 | // pass 'nil' as the third parameter. 33 | req, err := http.NewRequest("GET", "", nil) 34 | if err != nil { 35 | t.Fatal(err) 36 | } 37 | req.Header.Set("Accept", "application/stix+json") 38 | 39 | var objectsSrv ServerHandlerType 40 | objectsSrv.CollectionID = "1234" 41 | objectsSrv.DS = &dummydb{} 42 | 43 | // We create a ResponseRecorder (which satisfies http.ResponseWriter) to record the response. 44 | rr := httptest.NewRecorder() 45 | handler := http.HandlerFunc(objectsSrv.ObjectsServerHandler) 46 | 47 | // Our handlers satisfy http.Handler, so we can call their ServeHTTP method 48 | // directly and pass in our Request and ResponseRecorder. 49 | handler.ServeHTTP(rr, req) 50 | 51 | // Check the status code is what we expect. 52 | if status := rr.Code; status != http.StatusOK { 53 | t.Errorf("handler returned wrong status code: got %v want %v", 54 | status, http.StatusOK) 55 | } 56 | 57 | // Check the response body is what we expect. 58 | expected := `{"type":"bundle","id":"bundle--1234","spec_version":"2.0"}` + "\n" 59 | 60 | if rr.Body.String() != expected { 61 | t.Errorf("handler returned unexpected body: got %v want %v", 62 | rr.Body.String(), expected) 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /internal/handlers/server.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015-2018 Bret Jordan, All rights reserved. 2 | // 3 | // Use of this source code is governed by an Apache 2.0 license 4 | // that can be found in the LICENSE file in the root of the source tree. 5 | 6 | package handlers 7 | 8 | import ( 9 | "os" 10 | "strings" 11 | 12 | "github.com/freetaxii/libstix2/datastore" 13 | "github.com/freetaxii/libstix2/resources/apiroot" 14 | "github.com/freetaxii/libstix2/resources/collections" 15 | "github.com/freetaxii/libstix2/resources/discovery" 16 | "github.com/freetaxii/libstix2/stixid" 17 | "github.com/freetaxii/libstix2/timestamp" 18 | "github.com/freetaxii/server/internal/config" 19 | "github.com/gologme/log" 20 | ) 21 | 22 | // -------------------------------------------------- 23 | // Setup Handler Struct 24 | // -------------------------------------------------- 25 | 26 | /* 27 | ServerHandler - This type will hold the data elements required to process 28 | all TAXII requests. 29 | */ 30 | type ServerHandler struct { 31 | Logger *log.Logger 32 | URLPath string // Used in HTML output and to build the URL for the next resource. 33 | HTMLEnabled bool // Is HTML output enabled for this service 34 | HTMLTemplate string // The full file path (prefix + HTML template directory + template filename) 35 | CollectionID string // The collection ID that is being used 36 | ServerRecordLimit int // The maximum number of records that the server will respond with. 37 | Authenticated bool // Is this handler to be authenticated 38 | BasicAuth bool // Is Basic Auth used 39 | DS datastore.Datastorer 40 | Resource interface{} // This holds the actual resource and is populated in the main freetaxii.go 41 | } 42 | 43 | // ---------------------------------------------------------------------- 44 | // These methods will copy the elements found in the main configuration file. 45 | // We do this so that we do not send the entire configuration to a handler. 46 | // Also, this enables us to create a generic handler that can fulfill requests 47 | // for all of the TAXII and STIX handlers because we can pre-format the data to 48 | // be in a consistent and correct from. 49 | // ---------------------------------------------------------------------- 50 | 51 | func New(logger *log.Logger) (ServerHandler, error) { 52 | var s ServerHandler 53 | 54 | if logger == nil { 55 | s.Logger = log.New(os.Stderr, "", log.LstdFlags) 56 | } else { 57 | s.Logger = logger 58 | } 59 | 60 | // TODO for right now lets just force this until we can plumb this in through the configuration file 61 | s.Authenticated = false 62 | s.BasicAuth = false 63 | 64 | return s, nil 65 | } 66 | 67 | /* 68 | NewDiscoveryHandler - This function will prepare the data for the Discovery handler. 69 | */ 70 | func NewDiscoveryHandler(logger *log.Logger, c config.DiscoveryService, r discovery.Discovery) (ServerHandler, error) { 71 | s, _ := New(logger) 72 | s.URLPath = c.Path 73 | s.HTMLEnabled = c.HTML.Enabled.Value 74 | s.HTMLTemplate = c.HTML.FullTemplatePath + c.HTML.TemplateFiles.Discovery.Value 75 | s.Resource = r 76 | return s, nil 77 | } 78 | 79 | /* 80 | NewAPIRootHandler - This function will prepare the data for the API Root handler. 81 | */ 82 | func NewAPIRootHandler(logger *log.Logger, api config.APIRootService, r apiroot.APIRoot) (ServerHandler, error) { 83 | s, _ := New(logger) 84 | s.URLPath = api.Path 85 | s.HTMLEnabled = api.HTML.Enabled.Value 86 | s.HTMLTemplate = api.HTML.FullTemplatePath + api.HTML.TemplateFiles.APIRoot.Value 87 | s.Resource = r 88 | return s, nil 89 | } 90 | 91 | /* 92 | NewCollectionsHandler - This function will prepare the data for the Collections handler. 93 | */ 94 | func NewCollectionsHandler(logger *log.Logger, api config.APIRootService, r collections.Collections, limit int) (ServerHandler, error) { 95 | s, _ := New(logger) 96 | s.URLPath = api.Path + "collections/" 97 | s.HTMLEnabled = api.HTML.Enabled.Value 98 | s.HTMLTemplate = api.HTML.FullTemplatePath + api.HTML.TemplateFiles.Collections.Value 99 | s.Resource = r 100 | s.ServerRecordLimit = limit 101 | return s, nil 102 | } 103 | 104 | /* 105 | NewCollectionHandler - This function will prepare the data for the Collection handler. 106 | */ 107 | func NewCollectionHandler(logger *log.Logger, api config.APIRootService, r collections.Collection, limit int) (ServerHandler, error) { 108 | s, _ := New(logger) 109 | s.URLPath = api.Path + "collections/" + r.ID + "/" 110 | s.HTMLEnabled = api.HTML.Enabled.Value 111 | s.HTMLTemplate = api.HTML.FullTemplatePath + api.HTML.TemplateFiles.Collection.Value 112 | s.Resource = r 113 | s.ServerRecordLimit = limit 114 | return s, nil 115 | } 116 | 117 | /* 118 | NewObjectsHandler - This function will prepare the data for the Objects handler. 119 | */ 120 | func NewObjectsHandler(logger *log.Logger, api config.APIRootService, collectionID string, limit int) (ServerHandler, error) { 121 | s, _ := New(logger) 122 | s.URLPath = api.Path + "collections/" + collectionID + "/objects/" 123 | s.HTMLEnabled = api.HTML.Enabled.Value 124 | s.HTMLTemplate = api.HTML.FullTemplatePath + api.HTML.TemplateFiles.Objects.Value 125 | s.CollectionID = collectionID 126 | s.ServerRecordLimit = limit 127 | return s, nil 128 | } 129 | 130 | /* 131 | NewObjectsByIDHandler - This function will prepare the data for the Objects by ID handler. 132 | */ 133 | func NewObjectsByIDHandler(logger *log.Logger, api config.APIRootService, collectionID string, limit int) (ServerHandler, error) { 134 | s, _ := New(logger) 135 | s.URLPath = api.Path + "collections/" + collectionID + "/objects/{objectid}/" 136 | s.HTMLEnabled = api.HTML.Enabled.Value 137 | s.HTMLTemplate = api.HTML.FullTemplatePath + api.HTML.TemplateFiles.Objects.Value 138 | s.CollectionID = collectionID 139 | s.ServerRecordLimit = limit 140 | return s, nil 141 | } 142 | 143 | /* 144 | NewObjectVersionsHandler - This function will prepare the data for the Objects by ID handler. 145 | */ 146 | func NewObjectVersionsHandler(logger *log.Logger, api config.APIRootService, collectionID string, limit int) (ServerHandler, error) { 147 | s, _ := New(logger) 148 | s.URLPath = api.Path + "collections/" + collectionID + "/objects/{objectid}/versions/" 149 | s.HTMLEnabled = api.HTML.Enabled.Value 150 | s.HTMLTemplate = api.HTML.FullTemplatePath + api.HTML.TemplateFiles.Versions.Value 151 | s.CollectionID = collectionID 152 | s.ServerRecordLimit = limit 153 | return s, nil 154 | } 155 | 156 | /* 157 | NewManifestHandler - This function will prepare the data for the Manifest handler. 158 | */ 159 | func NewManifestHandler(logger *log.Logger, api config.APIRootService, collectionID string, limit int) (ServerHandler, error) { 160 | s, _ := New(logger) 161 | s.URLPath = api.Path + "collections/" + collectionID + "/manifest/" 162 | s.HTMLEnabled = api.HTML.Enabled.Value 163 | s.HTMLTemplate = api.HTML.FullTemplatePath + api.HTML.TemplateFiles.Manifest.Value 164 | s.CollectionID = collectionID 165 | s.ServerRecordLimit = limit 166 | return s, nil 167 | } 168 | 169 | // ---------------------------------------------------------------------- 170 | // Private Methods - ServerHandler 171 | // ---------------------------------------------------------------------- 172 | 173 | /* 174 | processURLParameters - This method will process all of the URL parameters from 175 | an HTTP request. 176 | */ 177 | func (s *ServerHandler) processURLParameters(q *collections.CollectionQuery, values map[string][]string) error { 178 | 179 | if values["added_after"] != nil { 180 | after := strings.Split(values["added_after"][0], ",") 181 | for _, v := range after { 182 | if timestamp.Valid(v) { 183 | q.AddedAfter = append(q.AddedAfter, v) 184 | } 185 | } 186 | } 187 | 188 | if values["added_before"] != nil { 189 | before := strings.Split(values["added_before"][0], ",") 190 | for _, v := range before { 191 | if timestamp.Valid(v) { 192 | q.AddedBefore = append(q.AddedBefore, v) 193 | } 194 | } 195 | } 196 | 197 | if values["limit"] != nil { 198 | q.Limit = strings.Split(values["limit"][0], ",") 199 | } 200 | 201 | if values["match[id]"] != nil { 202 | ids := strings.Split(values["match[id]"][0], ",") 203 | for _, v := range ids { 204 | if stixid.ValidSTIXID(v) { 205 | q.STIXID = append(q.STIXID, v) 206 | } 207 | } 208 | } 209 | 210 | if values["match[type]"] != nil { 211 | objTypes := strings.Split(values["match[type]"][0], ",") 212 | for _, v := range objTypes { 213 | if stixid.ValidSTIXObjectType(v) { 214 | q.STIXType = append(q.STIXType, v) 215 | } 216 | } 217 | } 218 | 219 | if values["match[version]"] != nil { 220 | vers := strings.Split(values["match[version]"][0], ",") 221 | for _, v := range vers { 222 | if v == "all" || v == "last" || v == "first" { 223 | q.STIXVersion = append(q.STIXVersion, v) 224 | } else if timestamp.Valid(v) { 225 | q.STIXVersion = append(q.STIXVersion, v) 226 | } 227 | } 228 | } 229 | 230 | // This list needs to also be updated in t_collectindataManifest.go : sqlCollectionDataWhereSpecVersion 231 | if values["match[spec_version]"] != nil { 232 | specV := strings.Split(values["match[spec_version]"][0], ",") 233 | for _, v := range specV { 234 | switch v { 235 | case "2.0": 236 | q.SpecVersion = append(q.SpecVersion, v) 237 | case "2.1": 238 | q.SpecVersion = append(q.SpecVersion, v) 239 | } 240 | } 241 | } 242 | 243 | s.Logger.Debugln("DEBUG: URL Parameter ID", q.STIXID) 244 | s.Logger.Debugln("DEBUG: URL Parameter Type", q.STIXType) 245 | s.Logger.Debugln("DEBUG: URL Parameter Version", q.STIXVersion) 246 | s.Logger.Debugln("DEBUG: URL Parameter Spec Version", q.SpecVersion) 247 | s.Logger.Debugln("DEBUG: URL Parameter Added After", q.AddedAfter) 248 | s.Logger.Debugln("DEBUG: URL Parameter Added Before", q.AddedBefore) 249 | s.Logger.Debugln("DEBUG: URL Parameter Limit", q.Limit) 250 | 251 | return nil 252 | } 253 | -------------------------------------------------------------------------------- /internal/handlers/taxiiHandler.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015-2018 Bret Jordan, All rights reserved. 2 | // 3 | // Use of this source code is governed by an Apache 2.0 license 4 | // that can be found in the LICENSE file in the root of the source tree. 5 | 6 | package handlers 7 | 8 | import ( 9 | "encoding/json" 10 | "html/template" 11 | "net/http" 12 | 13 | "github.com/freetaxii/libstix2/defs" 14 | "github.com/freetaxii/server/internal/headers" 15 | ) 16 | 17 | /* 18 | DiscoveryHandler - This method will handle all Discovery requests 19 | */ 20 | func (s *ServerHandler) DiscoveryHandler(w http.ResponseWriter, r *http.Request) { 21 | s.Logger.Infoln("INFO: Found Discovery request from", r.RemoteAddr, "at", r.RequestURI) 22 | s.baseHandler(w, r) 23 | } 24 | 25 | /* 26 | APIRootHandler - This method will handle all API Root requests 27 | */ 28 | func (s *ServerHandler) APIRootHandler(w http.ResponseWriter, r *http.Request) { 29 | s.Logger.Infoln("INFO: Found API Root request from", r.RemoteAddr, "at", r.RequestURI) 30 | s.baseHandler(w, r) 31 | } 32 | 33 | /* 34 | CollectionsHandler - This method will handle all Collections requests 35 | */ 36 | func (s *ServerHandler) CollectionsHandler(w http.ResponseWriter, r *http.Request) { 37 | s.Logger.Infoln("INFO: Found Collections request from", r.RemoteAddr, "at", r.RequestURI) 38 | s.baseHandler(w, r) 39 | } 40 | 41 | /* 42 | CollectionHandler - This method will handle all Collection requests 43 | */ 44 | func (s *ServerHandler) CollectionHandler(w http.ResponseWriter, r *http.Request) { 45 | s.Logger.Infoln("INFO: Found Collection request from", r.RemoteAddr, "at", r.RequestURI) 46 | s.baseHandler(w, r) 47 | } 48 | 49 | /* 50 | baseHandler - This method handles all requests for the following TAXII 51 | media type responses: Discovery, API-Root, Collections, and Collection 52 | */ 53 | func (s *ServerHandler) baseHandler(w http.ResponseWriter, r *http.Request) { 54 | 55 | // If trace is enabled in the logger, than decode the HTTP Request to the log 56 | if s.Logger.GetLevel("trace") { 57 | headers.DebugHttpRequest(r) 58 | } 59 | 60 | // -------------------------------------------------- 61 | // 1st Check Authentication 62 | // -------------------------------------------------- 63 | 64 | // If authentication is required and the client does not provide credentials 65 | // or their credentials do not match, then send an error message. 66 | // We need to return right here as to prevent further processing. 67 | if s.Authenticated == true { 68 | s.Logger.Debugln("DEBUG: Authentication Enabled") 69 | if s.BasicAuth == true { 70 | s.Logger.Debugln("DEBUG: Basic Authentication Enabled") 71 | w.Header().Set("WWW-Authenticate", `Basic realm="Authentication Required"`) 72 | if success := s.authenticate(r.BasicAuth()); success != true { 73 | s.Logger.Debugln("DEBUG: Authentication failed for", r.RemoteAddr, "at", r.RequestURI) 74 | s.sendUnauthenticatedError(w) 75 | return 76 | } 77 | } else { 78 | // If authentication is enabled, but basic is not, then fail since 79 | // no other authentication is currently enabled. 80 | s.Logger.Debugln("DEBUG: Authentication method from", r.RemoteAddr, "at", r.RequestURI, "not supported") 81 | s.sendUnauthenticatedError(w) 82 | return 83 | } 84 | } // End Authentication Check 85 | 86 | // -------------------------------------------------- 87 | // Check Accept Header Media Type 88 | // -------------------------------------------------- 89 | var acceptHeader headers.MediaType 90 | acceptHeader.ParseTAXII(r.Header.Get("Accept")) 91 | 92 | // -------------------------------------------------- 93 | // Encode outgoing response message 94 | // -------------------------------------------------- 95 | 96 | // Set header for TLS 97 | w.Header().Add("Strict-Transport-Security", "max-age=86400; includeSubDomains") 98 | 99 | if acceptHeader.TAXII21 == true { 100 | // Setup JSON stream encoder 101 | j := json.NewEncoder(w) 102 | w.Header().Set("Content-Type", defs.MEDIA_TYPE_TAXII21) 103 | w.WriteHeader(http.StatusOK) 104 | j.Encode(s.Resource) 105 | 106 | } else if acceptHeader.JSON == true { 107 | // Setup JSON stream encoder 108 | j := json.NewEncoder(w) 109 | w.Header().Set("Content-Type", defs.MEDIA_TYPE_JSON) 110 | w.WriteHeader(http.StatusOK) 111 | j.SetIndent("", " ") 112 | j.Encode(s.Resource) 113 | 114 | } else if s.HTMLEnabled == true && acceptHeader.HTML == true { 115 | w.Header().Set("Content-Type", defs.MEDIA_TYPE_HTML) 116 | w.WriteHeader(http.StatusOK) 117 | 118 | // ---------------------------------------------------------------------- 119 | // Setup HTML Template 120 | // ---------------------------------------------------------------------- 121 | htmlTemplateResource := template.Must(template.ParseFiles(s.HTMLTemplate)) 122 | htmlTemplateResource.Execute(w, s) 123 | 124 | } else { 125 | s.sendNotAcceptableError(w) 126 | return 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /internal/headers/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015-2018 Bret Jordan, All rights reserved. 2 | // 3 | // Use of this source code is governed by an Apache 2.0 license 4 | // that can be found in the LICENSE file in the root of the source tree. 5 | 6 | package headers 7 | -------------------------------------------------------------------------------- /internal/headers/headers.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015-2018 Bret Jordan, All rights reserved. 2 | // 3 | // Use of this source code is governed by an Apache 2.0 license 4 | // that can be found in the LICENSE file in the root of the source tree. 5 | 6 | package headers 7 | 8 | import ( 9 | "net/http" 10 | "strings" 11 | 12 | "github.com/gologme/log" 13 | ) 14 | 15 | type MediaType struct { 16 | TAXII21 bool 17 | TAXII20 bool 18 | STIX21 bool 19 | STIX20 bool 20 | HTML bool 21 | JSON bool 22 | } 23 | 24 | func (h *MediaType) ParseTAXII(media string) { 25 | // If there are spaces after the semicolon, remove all of them 26 | a := strings.Replace(media, "; ", ";", -1) 27 | a1 := strings.Split(a, ",") 28 | 29 | for _, v := range a1 { 30 | if v == "*" || v == "*/*" || v == "application/taxii+json" || v == "application/taxii+json;version=2.1" { 31 | h.TAXII21 = true 32 | } else if v == "application/vnd.oasis.taxii+json" || v == "application/vnd.oasis.taxii+json;version=2.0" { 33 | h.TAXII20 = true 34 | } else if strings.Contains(v, "application/json") { 35 | h.JSON = true 36 | } else if strings.Contains(v, "text/html") { 37 | h.HTML = true 38 | } 39 | } 40 | } 41 | 42 | func (h *MediaType) ParseSTIX(media string) { 43 | a := strings.Replace(media, " ", "", -1) 44 | a1 := strings.Split(a, ",") 45 | 46 | for _, v := range a1 { 47 | if v == "*" || v == "*/*" || v == "application/stix+json" || v == "application/stix+json;version=2.1" { 48 | h.STIX21 = true 49 | } else if v == "application/vnd.oasis.stix+json" || v == "application/vnd.oasis.stix+json;version=2.0" { 50 | h.STIX20 = true 51 | } else if strings.Contains(v, "application/json") { 52 | h.JSON = true 53 | } else if strings.Contains(v, "text/html") { 54 | h.HTML = true 55 | } 56 | } 57 | } 58 | 59 | // -------------------------------------------------- 60 | // Debug HTTP Headers 61 | // -------------------------------------------------- 62 | 63 | func DebugHttpRequest(r *http.Request) { 64 | 65 | log.Traceln("DEBUG: --------------- BEGIN HTTP DUMP ---------------") 66 | log.Traceln("DEBUG: Method", r.Method) 67 | log.Traceln("DEBUG: URL", r.URL) 68 | log.Traceln("DEBUG: Proto", r.Proto) 69 | log.Traceln("DEBUG: ProtoMajor", r.ProtoMajor) 70 | log.Traceln("DEBUG: ProtoMinor", r.ProtoMinor) 71 | log.Traceln("DEBUG: Header", r.Header) 72 | log.Traceln("DEBUG: Body", r.Body) 73 | log.Traceln("DEBUG: ContentLength", r.ContentLength) 74 | log.Traceln("DEBUG: TransferEncoding", r.TransferEncoding) 75 | log.Traceln("DEBUG: Close", r.Close) 76 | log.Traceln("DEBUG: Host", r.Host) 77 | log.Traceln("DEBUG: Form", r.Form) 78 | log.Traceln("DEBUG: PostForm", r.PostForm) 79 | log.Traceln("DEBUG: MultipartForm", r.MultipartForm) 80 | log.Traceln("DEBUG: Trailer", r.Trailer) 81 | log.Traceln("DEBUG: RemoteAddr", r.RemoteAddr) 82 | log.Traceln("DEBUG: RequestURI", r.RequestURI) 83 | log.Traceln("DEBUG: TLS", r.TLS) 84 | log.Traceln("DEBUG: --------------- END HTTP DUMP ---------------") 85 | log.Traceln() 86 | log.Traceln("DEBUG: --------------- BEGIN HEADER DUMP ---------------") 87 | for k, v := range r.Header { 88 | log.Traceln("DEBUG:", k, v) 89 | } 90 | log.Traceln("DEBUG: --------------- END HEADER DUMP ---------------") 91 | } 92 | --------------------------------------------------------------------------------