diff --git a/.dockerignore b/.dockerignore
new file mode 100644
index 0000000..ef370c2
--- /dev/null
+++ b/.dockerignore
@@ -0,0 +1,3 @@
+.git
+node_modules/
+release/
diff --git a/.eslintignore b/.eslintignore
new file mode 100644
index 0000000..a261f29
--- /dev/null
+++ b/.eslintignore
@@ -0,0 +1 @@
+dist/*
diff --git a/Dockerfile b/Dockerfile
index 1cd1e52..41a925f 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,16 +1,10 @@
-FROM node:slim
+FROM node:alpine
MAINTAINER Vault-UI Contributors
-ADD package.json /tmp/package.json
-RUN cd /tmp && npm install --silent && mkdir -p /app/ && mv /tmp/node_modules /app/
-
-RUN npm install --silent -g webpack
-
ADD . /app
WORKDIR /app
-
-RUN npm run build
+RUN npm install --silent && npm run build-web && npm prune --silent --production
EXPOSE 8000
diff --git a/README.md b/README.md
index e9f1fe1..5baf568 100644
--- a/README.md
+++ b/README.md
@@ -1,34 +1,88 @@
-# Vault-UI
+
+
+
+
[![](https://images.microbadger.com/badges/image/djenriquez/vault-ui.svg)](https://microbadger.com/images/djenriquez/vault-ui)
[![Run Status](https://api.shippable.com/projects/581e7826fbc68c0f00deb0ca/badge?branch=master)](https://app.shippable.com/projects/581e7826fbc68c0f00deb0ca)
-A beautiful way to manage your secrets in Vault
-![Landing Page](images/Landing.png)
+# Vault-UI
+
+A beautiful way to manage your Hashicorp Vault
+
+![](http://i.imgur.com/COBxk3m.gif)
+
+## Features
+
+- Easy to deploy as Web App
+- Desktop version works on Mac, Linux and Windows
+- Material UI Design
+- Integrated JSON Editor
+- Written in React
+
+## Installation
+
+### Desktop Version
+
+Vault-UI Desktop is available for the following operating systems:
+- Windows
+- MacOS
+- Linux (32bit and 64bit AppImage)
+
+Download the latest version from the release page and install/run the software
+
+### Web Version
+
+Vault-UI can be deployed as a shared web app for your organization
-## Configuration
-Configuration is accessed by clicking on the configuration cog on the login page.
+Docker images are automatically built using an [automated build on Docker Hub](https://hub.docker.com/r/djenriquez/vault-ui/builds/).
+We encourage that versioned images are used for production.
-
+To run Vault-UI using the latest Docker image:
+```bash
+docker run -d \
+-p 8000:8000 \
+--name vault-ui \
+djenriquez/vault-ui
+```
-### Vault Endpoint
-Users can enter in the full endpoint to Vault, including scheme. When running the docker image, it is possible to
-set the following environment variables to pre-configure authentication settings:
+#### Advanced configuration options
+
+By default, connection and authentication parameters must be configured by clicking on the configuration cog on the login page.
+Using environment variables (via docker), an administrator can pre-configure those parameters.
+
+Example command to pre-configure the Vault server URL and authentication method
+```bash
+docker run -d \
+-p 8000:8000 \
+-e VAULT_URL_DEFAULT=http://vault.server.org:8200
+-e VAULT_AUTH_DEFAULT=GITHUB
+--name vault-ui \
+djenriquez/vault-ui
+```
+
+Supported environment variables:
+- `NODE_TLS_REJECT_UNAUTHORIZED` disable TLS server side validation (ex. vault deployed with self-signed certificate)
- `VAULT_URL_DEFAULT` will set the default vault endpoint.
- `VAULT_AUTH_DEFAULT` will set the default authentication method type. See below for supported authentication methods.
- `VAULT_AUTH_BACKEND_PATH` will set the default backend path. Useful when multiple backends of the same type are mounted on the vault file system.
+- `VAULT_SUPPLIED_TOKEN_HEADER` will instruct Vault-UI to attempt authentication using a token provided by the client in the specified HTTP request header.
This defaults can be overridden if the user fills out the endpoint and auth method manually.
-## Authentication
+
Currently supported authentication methods:
- `GITHUB` : When using the [GitHub](https://www.vaultproject.io/docs/auth/github.html) backend
- `USERNAMEPASSWORD` : When using the [Username & Password](https://www.vaultproject.io/docs/auth/userpass.html) or [RADIUS](https://www.vaultproject.io/docs/auth/radius.html) backends
- `LDAP` : When using the [LDAP](https://www.vaultproject.io/docs/auth/ldap.html) backend
- `TOKEN` : When using the [Tokens](https://www.vaultproject.io/docs/auth/token.html) backend
-### Token authentication by header (SSO)
+
In some cases, users might want to use middleware to authenticate into Vault-UI for purposes like SSO. In this case, the `VAULT_SUPPLIED_TOKEN_HEADER` may be populated with the name of the header that contains a token to be used for authentication.
+
+## Usage
+
### Basic policy for Vault-UI users
A user/token accessing Vault-UI requires a basic set of capabilities in order to correctly discover and display the various mounted backends.
Please make sure your user is granted a policy with at least the following permissions:
@@ -80,51 +134,24 @@ path "sys/auth" {
}
```
-## Secrets
-![Secrets Management](images/Home.png)
-
+### Secrets
Secrets are now managed using the graphical [josdejong/jsoneditor](https://github.com/josdejong/jsoneditor) JSON editor. Schema validation is enforced on policies to aid the operator in writing correct syntax.
-
-Secrets also are accessible directly by key from a browser by navigating to the URI `/secrets///key`. For example, if you have a generic secret key of /hello/world/vault using the generic mount `secret`, one can navigate to this directly through http://vault-ui.myorg.com/secrets/secret/hello/world/vault.
+Secrets also are accessible directly by key from a browser by navigating to the URI `/secrets///key`. For example, if you have a generic secret key of /hello/world/vault using the _generic_ mount `secret/`, one can navigate to this directly through http://vault-ui.myorg.com/secrets/secret/hello/world/vault.
-### Root key bias
+#### Root key bias
By default, secrets will display as their raw JSON value represented by the `data` field in the HTTP GET response metadata. However, users can apply a "Root Key" bias to the secrets through the settings page. The "Root Key" will be used when reading, creating and updating secrets such that the value displayed in the UI is the value stored at the "Root Key". For example, if the secret at `secret/hello` is `{ "value": "world" }`, setting the "Root Key" to `value` will update the UI such that the secret will display as simply "world" instead of `{ "value": "world" }`.
-
-## Policies
+### Policies
Policies are managed also using the [josdejong/jsoneditor](https://github.com/josdejong/jsoneditor) JSON editor. Currently, GitHub and raw Tokens are the only supported authentication backends for associated policies.
-## Token Management
-
+### Token Management
+Users have the ability to create and revoke tokens, manage token roles and list accessors.
-Users now have the ability to create and revoke tokens.
-
+### Response Wrapping
+Vault-UI supports response-wrapping of secrets in _generic_ backends. Wrapping custom JSON data is also supported.
-## Response Wrapping
-Vault-UI supports response-wrapping raw values. It currently does not support wrapping of existing secrets.
-
-
-## Run
-Vault-UI Docker images are automatically built using an [automated build on Docker Hub](https://hub.docker.com/r/djenriquez/vault-ui/builds/). We encourage that versioned images are used for production.
-To run Vault-UI using the latest Docker image:
-```bash
-docker run -d \
--p 8000:8000 \
---name vault-ui \
-djenriquez/vault-ui
-```
-
-### Skip TLS Verification
-In the case that you need to skip TLS verification, say for self-signed certs, you can run Vault-UI with the environment variable `NODE_TLS_REJECT_UNAUTHORIZED=0`:
-```
-docker run -d \
--p 8000:8000 \
--e NODE_TLS_REJECT_UNAUTHORIZED=0 \
---name vault-ui \
-djenriquez/vault-ui
-```
## Development
@@ -143,12 +170,8 @@ The following will spin up a Vault UI server only. It will not set up
Vault for you:
```sh
npm install
-
-# If you do not have webpack installed globally
-npm install -g webpack
-
+npm run dev-pack &
npm start
-webpack -w
```
# Licensing
diff --git a/app/App.jsx b/app/App.jsx
index 5ca72ef..4e851c7 100644
--- a/app/App.jsx
+++ b/app/App.jsx
@@ -1,10 +1,12 @@
import React from 'react'
+import axios from 'axios';
import ReactDOM from 'react-dom';
import Login from './components/Login/Login.jsx';
-import { Router, Route, browserHistory } from 'react-router'
+import { Router, Route } from 'react-router'
import injectTapEventPlugin from 'react-tap-event-plugin';
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
import getMuiTheme from 'material-ui/styles/getMuiTheme';
+import { history } from './components/shared/VaultUtils.jsx';
import App from './components/App/App.jsx';
import SecretsGeneric from './components/Secrets/Generic/Generic.jsx';
import PolicyManager from './components/Policies/Manage.jsx';
@@ -16,6 +18,10 @@ import GithubAuthBackend from './components/Authentication/Github/Github.jsx';
import RadiusAuthBackend from './components/Authentication/Radius/Radius.jsx';
import SecretUnwrapper from './components/shared/Wrapping/Unwrapper';
+// Load here to signal webpack
+import 'flexboxgrid/dist/flexboxgrid.min.css';
+import './assets/favicon.ico';
+
injectTapEventPlugin();
(function () {
@@ -34,6 +40,22 @@ injectTapEventPlugin();
window.CustomEvent = CustomEvent;
})();
+const checkVaultUiServer = (nextState, replace, callback) => {
+ // If it's a web deployment, query the server for default connection parameters
+ // Those can be set using environment variables in the nodejs process
+ if (WEBPACK_DEF_TARGET_WEB) {
+ axios.get('/vaultui').then((resp) => {
+ window.defaultVaultUrl = resp.data.defaultVaultUrl;
+ window.defaultAuthMethod = resp.data.defaultAuthMethod;
+ window.defaultBackendPath = resp.data.defaultBackendPath;
+ window.suppliedAuthToken = resp.data.suppliedAuthToken;
+ callback();
+ }).catch((err) => callback())
+ } else {
+ callback();
+ }
+}
+
const checkAccessToken = (nextState, replace, callback) => {
let vaultAuthToken = window.localStorage.getItem('vaultAccessToken');
if (!vaultAuthToken) {
@@ -49,8 +71,8 @@ const muiTheme = getMuiTheme({
ReactDOM.render((
-
-
+
+
diff --git a/app/assets/favicon.ico b/app/assets/favicon.ico
new file mode 100644
index 0000000..a623824
Binary files /dev/null and b/app/assets/favicon.ico differ
diff --git a/app/assets/vault-ui-logo.svg b/app/assets/vault-ui-logo.svg
new file mode 100644
index 0000000..65bdb0a
--- /dev/null
+++ b/app/assets/vault-ui-logo.svg
@@ -0,0 +1,236 @@
+
+
diff --git a/app/components/App/App.jsx b/app/components/App/App.jsx
index 1189f43..af147dc 100644
--- a/app/components/App/App.jsx
+++ b/app/components/App/App.jsx
@@ -7,16 +7,15 @@ import Snackbar from 'material-ui/Snackbar';
import Dialog from 'material-ui/Dialog';
import FlatButton from 'material-ui/FlatButton';
import Paper from 'material-ui/Paper';
-import { browserHistory } from 'react-router';
import Warning from 'material-ui/svg-icons/alert/warning';
import { green500, red500 } from 'material-ui/styles/colors.js'
import styles from './app.css';
import JsonEditor from '../shared/JsonEditor.jsx';
import { Card, CardHeader, CardText } from 'material-ui/Card';
-import { callVaultApi, tokenHasCapabilities } from '../shared/VaultUtils.jsx'
+import { callVaultApi, tokenHasCapabilities, history } from '../shared/VaultUtils.jsx'
-let twoMinuteWarningTimeout;
-let logoutTimeout;
+let twoMinuteWarningTimeoutId;
+let logoutTimeoutId;
function snackBarMessage(message) {
let ev = new CustomEvent("snackbar", { detail: { message: message } });
@@ -67,7 +66,7 @@ export default class App extends React.Component {
}
let logoutTimeout = () => {
- browserHistory.push('/login');
+ history.push('/login');
}
// Retrieve session identity information
@@ -78,15 +77,17 @@ export default class App extends React.Component {
let ttl = resp.data.data.ttl * 1000;
// The upper limit of setTimeout is 0x7FFFFFFF (or 2147483647 in decimal)
if (ttl > 0 && ttl < 2147483648) {
- setTimeout(logoutTimeout, ttl);
- setTimeout(twoMinuteWarningTimeout, ttl - TWO_MINUTES);
+ clearTimeout(logoutTimeoutId);
+ clearTimeout(twoMinuteWarningTimeoutId);
+ logoutTimeoutId = setTimeout(logoutTimeout, ttl);
+ twoMinuteWarningTimeoutId = setTimeout(twoMinuteWarningTimeout, ttl - TWO_MINUTES);
}
}
})
.catch((err) => {
if (_.has(err, 'response.status') && err.response.status >= 400) {
window.localStorage.removeItem('vaultAccessToken');
- browserHistory.push(`/login?returnto=${encodeURI(this.props.location.pathname)}`);
+ history.push(`/login?returnto=${encodeURI(this.props.location.pathname)}`);
} else throw err;
});
}
@@ -119,11 +120,11 @@ export default class App extends React.Component {
this.reloadSessionIdentity();
// Check access to the sys/capabilities-self path
- callVaultApi('post', 'sys/capabilities-self', null, {path: '/'})
- .then(() =>{
+ callVaultApi('post', 'sys/capabilities-self', null, { path: '/' })
+ .then(() => {
this.setState({ tokenCanQueryCapabilities: true });
})
- .catch(() =>{
+ .catch(() => {
this.setState({ tokenCanQueryCapabilities: false });
})
@@ -138,8 +139,8 @@ export default class App extends React.Component {
}
componentWillUnmount() {
- clearTimeout(logoutTimeout);
- clearTimeout(twoMinuteWarningTimeout);
+ clearTimeout(logoutTimeoutId);
+ clearTimeout(twoMinuteWarningTimeoutId);
}
renderSessionExpDialog() {
@@ -258,7 +259,7 @@ export default class App extends React.Component {
-
Get started by using the left menu to navigate your Vault
+
Get started by using the left menu to navigate your Vault