Skip to content

Commit

Permalink
feat: Implement config auto-generation and docker volumes (#18)
Browse files Browse the repository at this point in the history
  • Loading branch information
TheButlah authored Nov 15, 2024
1 parent 8fc1e95 commit 3b7e57f
Show file tree
Hide file tree
Showing 8 changed files with 191 additions and 97 deletions.
3 changes: 2 additions & 1 deletion .github/workflows/container.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ jobs:
uses: actions/[email protected]
with:
name: rust
path: artifacts
path: ./identity-server/artifacts

- name: Build Image
id: build-image
Expand All @@ -28,6 +28,7 @@ jobs:
tags: commit-${{ github.sha }} ${{ inputs.additional-tags }}
platforms: linux/arm64,linux/amd64,windows/amd64
oci: true
context: ./identity-server
containerfiles: |
./identity-server/Dockerfile
Expand Down
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
# For local development
.direnv
.envrc
my_config.toml
my-config.toml

# SQLite
*.db
Empty file added identity-server/.empty
Empty file.
23 changes: 15 additions & 8 deletions identity-server/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,25 @@ ARG TARGETPLATFORM

COPY --from=distroless /etc/passwd /etc/passwd
COPY --from=distroless /etc/group /etc/group
USER nonroot
USER nonroot:nonroot
ENV USER=nonroot

ENV XDG_CACHE_HOME=/home/nonroot/.cache
VOLUME ["/home/nonroot/.cache"]
ENV XDG_CACHE_HOME=/var/.cache
# Only here to create the .cache folder
COPY --chmod=644 --chown=nonroot:nonroot .empty /var/.cache/.empty
VOLUME ["/var"]
WORKDIR ["/var"]

ENV XDG_CONFIG_HOME=/etc/cfg
COPY --chmod=644 --chown=nonroot:nonroot ./default-config.toml /etc/cfg/config.toml
VOLUME ["/etc/cfg"]

# Bring in the actual binary we will run
COPY --from=distroless --chmod=544 --chown=nonroot:nonroot /artifacts/$TARGETPLATFORM/identity-server /opt/identity-server
ENTRYPOINT ["/opt/identity-server"]
VOLUME ["/var/db"]
WORKDIR ["/var/db"]

EXPOSE 443/tcp
EXPOSE 80/tcp
EXPOSE 8443/tcp
EXPOSE 8443/udp

ENV RUST_BACKTRACE=1
ENTRYPOINT ["/opt/identity-server"]
CMD ["serve", "--config", "/etc/cfg/config.toml"]
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,16 @@
# Note: When using TLS, we will always send the HSTS header to force clients to only
# use https urls.
[http]
port = 443 # also supports 0 to mean random
port = 8443 # also supports 0 to mean random

# Settings related to configuring TLS certificates. In most cases, the "acme" type is
# the simplest to set up.
[http.tls]
type = "acme" # requires publicly visible port to be 443, otherwise the challenge fails
type = "acme" # publicly visible port MUST be 443, otherwise the challenge fails
domains = [] # You must fill this in with your public domain name(s).
# domains = ["socialvr.net", "socialvr.net:1337", "10.11.12.13"]
is_prod = true # we are using LetsEncrypt's main, production directory.
email = "" # optional: you can fill in your email address here
# domains = ["socialvr.net", "socialvr.net:1337", "10.11.12.13"]

# [http.tls]
# type = "disable" # disables TLS and everything will use HTTP instead.
Expand Down
1 change: 0 additions & 1 deletion identity-server/justfile
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ rust-build:

# Copy artifacts from cargo target dir into to the artifacts directory
artifacts: rust-build
pwd
mkdir -p {{artifacts_dir}}
cp {{target_dir}}/aarch64-apple-darwin/artifact/{{package_name}} {{artifacts_dir}}/{{package_name}}-macos-aarch64
cp {{target_dir}}/x86_64-unknown-linux-musl/artifact/{{package_name}} {{artifacts_dir}}/{{package_name}}-linux-x86_64
Expand Down
43 changes: 37 additions & 6 deletions identity-server/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use std::{path::PathBuf, str::FromStr};

use serde::{Deserialize, Serialize};

pub const DEFAULT_CONFIG_CONTENTS: &str = include_str!("../default_config.toml");
pub const DEFAULT_CONFIG_CONTENTS: &str = include_str!("../default-config.toml");
const CACHE_DIR_SUFFIX: &str = "nexus_identity_server";

#[derive(Serialize, Deserialize, Eq, PartialEq, Debug, Clone)]
Expand Down Expand Up @@ -54,6 +54,17 @@ pub struct HttpConfig {
pub tls: TlsConfig,
}

impl HttpConfig {
fn validate(&self) -> Result<(), ValidationError> {
if let TlsConfig::Acme { ref domains, .. } = self.tls {
if domains.is_empty() {
return Err(ValidationError::UnspecifiedDomain);
}
}
Ok(())
}
}

impl Default for HttpConfig {
fn default() -> Self {
Self {
Expand All @@ -65,7 +76,7 @@ impl Default for HttpConfig {

impl HttpConfig {
const fn default_port() -> u16 {
443
8443
}
}

Expand Down Expand Up @@ -131,10 +142,18 @@ fn default_some<T: Default>() -> Option<T> {
Some(T::default())
}

#[derive(Debug, thiserror::Error)]
#[derive(Debug, thiserror::Error, Eq, PartialEq)]
pub enum ConfigError {
#[error("error in toml file: {0}")]
#[error("error deserializing toml file: {0}")]
Toml(#[from] toml::de::Error),
#[error("config file was invalid: {0}")]
FailedValidation(#[from] ValidationError),
}

#[derive(Debug, thiserror::Error, Eq, PartialEq)]
pub enum ValidationError {
#[error("when using ACME tls, you *must* specify at least one domain")]
UnspecifiedDomain,
}

/// The contents of the config file. Contains all settings customizeable during
Expand All @@ -152,6 +171,14 @@ pub struct Config {
pub third_party: ThirdPartySettings,
}

impl Config {
/// Validates the deserialized config
pub fn validate(&self) -> Result<(), ValidationError> {
self.http.validate()?;
Ok(())
}
}

impl FromStr for Config {
type Err = ConfigError;

Expand All @@ -173,7 +200,7 @@ mod test {
db_file: PathBuf::from("./identities.db"),
},
http: HttpConfig {
port: 443,
port: 8443,
tls: TlsConfig::Acme {
email: String::new(),
domains: Vec::new(),
Expand All @@ -197,10 +224,14 @@ mod test {
}

#[test]
fn test_default_config_deserializes_correctly() {
fn test_default_config_deserializes_correctly_but_fails_validation() {
let deserialized: Config = toml::from_str(DEFAULT_CONFIG_CONTENTS)
.expect("default config file should always deserialize");
assert_eq!(deserialized, Config::default());
assert_eq!(
deserialized.validate(),
Err(ValidationError::UnspecifiedDomain)
)
}

#[test]
Expand Down
Loading

0 comments on commit 3b7e57f

Please sign in to comment.