Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create initial version for signing certs with KMS key #5

Merged
merged 32 commits into from
Jul 27, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
49241b4
Create initial version for signing certs with KMS key
tlbdk Jan 30, 2020
7859a40
Fix test
tlbdk Feb 1, 2020
5e7cc17
Get initial cert working
tlbdk Feb 1, 2020
cbd1545
Get initial logic for signing service working
tlbdk Feb 9, 2020
89db017
Fix challengeValue json marshaling
tlbdk Feb 9, 2020
7b5c16c
Get http signing server working
tlbdk May 7, 2020
6f5c32e
Fix linting
tlbdk May 7, 2020
0193674
Restructure
tlbdk May 14, 2020
bea20ed
Remove dependency on KSMSigner
tlbdk May 14, 2020
edf9f96
Cleanup
tlbdk May 14, 2020
05f4798
Add debug info for cloudbuilder
tlbdk May 14, 2020
cee78e6
Escape
tlbdk May 14, 2020
2f9f126
Revert workarounds
tlbdk May 14, 2020
7ee72dc
Use ssh.AlgorithmSigner as the basis for the keyring to make things a…
tlbdk May 15, 2020
4ffa322
Clean up cloudbuild
tlbdk May 15, 2020
c2ec18f
Fix docker cmd
tlbdk May 15, 2020
8e74220
Fix name in build
tlbdk May 15, 2020
353a657
Fix dir
tlbdk May 15, 2020
4fd5b8e
Add cleanup
tlbdk May 15, 2020
1066502
Start limiting the user cert issued
tlbdk May 17, 2020
9716ed3
Fix more TODOs
tlbdk May 18, 2020
38b2bea
Fix command handling
tlbdk May 18, 2020
81a20c8
Update README
tlbdk May 18, 2020
0595a9b
Limit max request and response size
tlbdk Jul 27, 2020
964ecf8
Read default issue cert lifetime from enviroment
tlbdk Jul 27, 2020
3a3af23
Update documentation
tlbdk Jul 27, 2020
6c75a56
Added TODOs and documentation
tlbdk Jul 27, 2020
8d02720
Error on duplicate pub keys
tlbdk Jul 27, 2020
253dc0c
Do more input valication
tlbdk Jul 27, 2020
de05c47
Ignore authorized_keys file
tlbdk Jul 27, 2020
5d821b1
Fix err check
tlbdk Jul 27, 2020
f7a2021
Add server image
tlbdk Jul 27, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,5 @@
.vscode
/auth-wrapper
vendor
/dist
authorized_keys
53 changes: 42 additions & 11 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -18,25 +18,56 @@ RUN go version

ENV GO111MODULE=on

RUN CGO_ENABLED=0 GOOS=linux go build -o auth-wrapper -ldflags "-X 'main.versionString=$VERSION'" ./cmd
RUN CGO_ENABLED=0 GOOS=linux go build -o auth-wrapper -ldflags "-X 'main.versionString=$VERSION'" ./cmd/authwrapper

# Production image
FROM ${WRAP_IMAGE} as production
RUN echo nobody:x:65534:65534:nobody:/: > password.minimal

#
# Auth-wrapper server image
#
FROM scratch as main

ARG WRAP_COMMAND
ARG WRAP_NAME
ARG SSH_KEY_PATH

COPY --from=builder /app/auth-wrapper /opt/bin/auth-wrapper
RUN ln -s /opt/bin/auth-wrapper /opt/bin/${WRAP_NAME}
COPY --from=builder /app/password.minimal /etc/password

# Used by git image
ENV GIT_SSH_COMMAND="ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no"
USER nobody

ENTRYPOINT ["/opt/bin/auth-wrapper"]

#
# Authwrapped git with KMS keys
#
FROM gcr.io/cloud-builders/git as git-kms

# Force google tools to use the DNS name so we can overwrite it in docker
ENV GCE_METADATA_HOST=metadata.google.internal
ARG SSH_KEY_PATH

COPY --from=builder /app/auth-wrapper /opt/bin/auth-wrapper
RUN ln -s /opt/bin/auth-wrapper /opt/bin/git

ENV GIT_SSH_COMMAND="ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no"

ENV PATH=/opt/bin:${PATH}
ENV WRAP_COMMAND=${WRAP_COMMAND}
ENV WRAP_COMMAND=git
ENV SSH_KEY_PATH=${SSH_KEY_PATH}
ENTRYPOINT ["/opt/bin/auth-wrapper"]


#
# Authwrapped git with local keys
#
FROM gcr.io/cloud-builders/git as git-local

COPY --from=builder /app/auth-wrapper /opt/bin/auth-wrapper
RUN ln -s /opt/bin/auth-wrapper /opt/bin/git

COPY build.pem /
RUN chmod 600 /build.pem

ENV GIT_SSH_COMMAND="ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no"

ENV PATH=/opt/bin:${PATH}
ENV WRAP_COMMAND=git
ENV SSH_KEY_PATH=/build.pem
ENTRYPOINT ["/opt/bin/auth-wrapper"]
122 changes: 75 additions & 47 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,87 +1,115 @@
# Auth wrapper

Simple wrapper that exposes an ssh-agent to all sub processes using keys from Google Cloud KMS or OpenSSH pem formated key.
Command wrapper that exposes an ssh-agent to all sub processes with keys and ssh certs backed by Google Cloud KMS or local OpenSSH pem formatted keys.

This can fx be used in CI/CD pipelines when checking code out, running package installers pulling code from private repos.
This can be used in:

* CI/CD pipelines when checking code out, running package installers pulling code from private repos.
* Auditing and restricting access to distributed SSH servers in a central location

## How to use

### Git checkout

Git clone with key store in Google Cloud KMS:

``` bash
export SSH_KEY_PATH=kms://projects/yourprojectname/locations/global/keyRings/yourkeyring/cryptoKeys/ssh-key/cryptoKeyVersions/1
auth-wrapper git clone git@github.com:connectedcars/private-module.git
```

Docker buildkit build with a key stored in Google Cloud KMS:
Git clone with local key:

``` bash
export SSH_KEY_PATH=kms://projects/yourprojectname/locations/global/keyRings/yourkeyring/cryptoKeys/ssh-key/cryptoKeyVersions/1
export PROGRESS_NO_TRUNC=1
export DOCKER_BUILDKIT=1
# The strings $SSH_AUTH_SOCK and $$SSH_AUTH_SOCK will be replaced with socket in the arguments
auth-wrapper docker --progress=plain --ssh=default='\$SSH_AUTH_SOCK' . # Note the escape to make sure we don't use the shells SSH_AUTH_SOCK
export SSH_KEY_PATH=build.pem
export SSH_KEY_PASSWORD=thepassword
auth-wrapper git clone git@github.com:connectedcars/private-module.git
```

[Dockerfile](./testdata/Dockerfile)

Google Cloud build with Docker buildkit build:

``` yaml
steps:
# Pull a modern version of docker
- name: 'gcr.io/cloud-builders/docker'
args: ['pull', 'gcr.io/cloud-builders/docker:latest']
# Workaround for https://github.com/moby/moby/issues/39120
- name: 'gcr.io/cloud-builders/docker'
args: ['pull', 'docker/dockerfile:experimental']
- name: 'gcr.io/cloud-builders/docker'
args: ['pull', 'docker/dockerfile:1.0-experimental']
# Build container injecting SSH agent socket
- name: 'gcr.io/$PROJECT_ID/auth-wrapper-docker.master:latest'
args: ['build', '--progress=plain', '--ssh=default=$$SSH_AUTH_SOCK', '-tag=gcr.io/$PROJECT_ID/$REPO_NAME.$BRANCH_NAME:$COMMIT_SHA', '.']
env:
- "SSH_KEY_PATH=kms://projects/$PROJECT_ID/locations/global/keyRings/cloudbuilder/cryptoKeys/ssh-key/cryptoKeyVersions/1"
- "PROGRESS_NO_TRUNC=1"
- "DOCKER_BUILDKIT=1"
images: ['gcr.io/$PROJECT_ID/$REPO_NAME.$BRANCH_NAME']
### SSH Certs

Signing server:

The signing server issues a certificate based on an allow list in authorized keys file format:

http://man7.org/linux/man-pages/man8/sshd.8.html#AUTHORIZED_KEYS_FILE_FORMAT

Example file:

authorized_keys:

``` text
# Only allow this public key access from 192.168.1.0/24 and to run command "echo hello" with principal name "user1,serverType"
restrict,command="echo hello",from="192.168.1.0/24",principals="user1,serverType" ecdsa-sha2-nistp256 AAAA...C (copy from output of client) user1@company.com
# Only allow this public key access with principal name "user2"
restrict,principals="user2" ssh-rsa AAAA...D(copy from output of client) user2@company.com
# Only allow sftp access with principal name "user3"
restrict,principals="user3",command=internal-sftp AAAA...E (copy from output of client) user3@company.com
```

Git clone with local key:
Starting the server:

``` bash
export SSH_KEY_PATH=build.pem
export SSH_KEY_PASSWORD=thepassword
auth-wrapper git clone git@github.com:connectedcars/private-module.git
export SSH_SIGNING_SERVER_LISTEN_ADDRESS=":3080"
export SSH_CA_KEY_PATH="kms://projects/yourprojectname/locations/global/keyRings/ssh-keys/cryptoKeys/ssh-key/cryptoKeyVersions/1"
export SSH_CA_AUTHORIZED_KEYS_PATH="authorized_keys"
export SSH_SIGNING_LIFETIME="60m"
auth-wrapper
```

Using the client:

``` bash
export SSH_KEY_PATH=kms://projects/yourprojectname/locations/global/keyRings/yourkeyring/cryptoKeys/ssh-key/cryptoKeyVersions/1
export SSH_SIGNING_SERVER_URL="http://localhost:3080"
auth-wrapper -p user1 ssh 1.2.3.4
auth-wrapper -p serverType:gw ssh 1.2.3.4 # Use wildcard match
```

SSH Server:

To configure a SSH server to trust the signing server CA for a specific user:

~/.ssh/authorized_keys:

``` text
cert-authority,principals="user1,serverType:gw" ssh-rsa AAAA...(copy from output of signing server) ca key
```

## Options

Environment variables:
### Arguments

* -principals : Principals to request

### Environment variables

Client options:

* SSH_KEY_PATH: Path to SSH key, can be OpenSSH PEM formated key or a url to KMS key
* SSH_KEY_PASSWORD: Password to key, only used by PEM formated key
* WRAP_COMMAND: Command to run with the arguments to auth-wrapper
* SSH_SIGNING_SERVER_URL: Url for the signing server
* SSH_PRINCIPALS: Principals to request

Signing server options:

* SSH_SIGNING_SERVER_LISTEN_ADDRESS: Listen address in the following format ":8080"
* SSH_CA_KEY_PATH: Path to CA signing key, only KMS keys supported at the moment and limited to "Elliptic Curve P-256 key
SHA256 Digest"
* SSH_CA_AUTHORIZED_KEYS_PATH": Path to authorized_keys following [AUTHORIZED_KEYS_FILE_FORMAT](http://man7.org/linux/man-pages/man8/sshd.8.html#AUTHORIZED_KEYS_FILE_FORMAT)

## Google Cloud KMS key setup

Create keyring and key:

``` bash
# Create keyring for cloud build keys
gcloud kms keyrings create --location global cloudbuild
# Create keyring
gcloud kms keyrings create --location global ssh-keys
# It needs to be be SHA512 as the ssh client seems to default to this hashing algorithm and KMS pairs key size and hashing algorithms for some reason.
gcloud kms keys create ssh-key --keyring cloudbuilder --location global --default-algorithm rsa-sign-pkcs1-4096-sha512 --purpose asymmetric-signing
gcloud kms keys create ssh-key --keyring ssh-keys --location global --default-algorithm rsa-sign-pkcs1-4096-sha512 --purpose asymmetric-signing
# Give cloud build access to use the key
gcloud kms keys add-iam-policy-binding ssh-key --keyring=cloudbuilder --location=global --member serviceAccount:projectserviceaccount@cloudbuild.gserviceaccount.com --role roles/cloudkms.signerVerifier
```

Extract public key and convert to ssh format:

``` bash
gcloud kms keys versions get-public-key 1 --key ssh-key --keyring=cloudbuilder --location=global > ssh-key.pem
# Copy the output to a github user
ssh-keygen -f ssh-key.pem -i -mPKCS8
gcloud kms keys add-iam-policy-binding ssh-key --keyring=ssh-keys --location=global --member user@company.com --role roles/cloudkms.signerVerifier
```

## Local key
Expand Down
91 changes: 24 additions & 67 deletions cloudbuild.yaml
Original file line number Diff line number Diff line change
@@ -1,65 +1,32 @@
steps:
# Pull a modern version of docker
- name: 'gcr.io/cloud-builders/docker'
args: ['pull', 'gcr.io/cloud-builders/docker:latest']
# Workaround for https://github.com/moby/moby/issues/39120
- name: 'gcr.io/cloud-builders/docker'
args: ['pull', 'docker/dockerfile:experimental']
- name: 'gcr.io/cloud-builders/docker'
args: ['pull', 'docker/dockerfile:1.0-experimental']
- name: 'gcr.io/cloud-builders/docker'
args: ['pull', 'docker.io/docker/dockerfile-copy:v0.1.9']
- name: 'gcr.io/cloud-builders/docker'
args: ['pull', 'ubuntu:19.10']
# Check version
- name: 'gcr.io/cloud-builders/docker'
args: ['version']
#
# Build KMS auth wrappers
#
# TODO: Move to own Dockerfile's, this is getting a bit too fancy
# Build auth wrapped git
# Build auth wrapped server container
- name: 'gcr.io/cloud-builders/docker'
args: [
'build',
'--build-arg=WRAP_IMAGE=gcr.io/cloud-builders/git',
'--build-arg=WRAP_COMMAND=/usr/bin/git',
'--build-arg=WRAP_NAME=git',
'--build-arg=SSH_KEY_PATH=kms://projects/connectedcars-staging/locations/global/keyRings/cloudbuilder/cryptoKeys/ssh-key/cryptoKeyVersions/3',
'--tag=gcr.io/$PROJECT_ID/$REPO_NAME-git.$BRANCH_NAME:$COMMIT_SHA',
'--tag=gcr.io/$PROJECT_ID/$REPO_NAME-git.$BRANCH_NAME:latest',
'--target=main',
'--tag=gcr.io/$PROJECT_ID/$REPO_NAME.$BRANCH_NAME:$COMMIT_SHA',
'--tag=gcr.io/$PROJECT_ID/$REPO_NAME.$BRANCH_NAME:latest',
'.'
]
# Build auth wrapped docker
# Build auth wrapped git
- name: 'gcr.io/cloud-builders/docker'
args: [
'build',
'--build-arg=WRAP_IMAGE=gcr.io/cloud-builders/docker',
'--build-arg=WRAP_COMMAND=/usr/bin/docker',
'--build-arg=WRAP_NAME=docker',
'--target=git-kms',
'--build-arg=SSH_KEY_PATH=kms://projects/connectedcars-staging/locations/global/keyRings/cloudbuilder/cryptoKeys/ssh-key/cryptoKeyVersions/3',
'--tag=gcr.io/$PROJECT_ID/$REPO_NAME-docker.$BRANCH_NAME:$COMMIT_SHA',
'--tag=gcr.io/$PROJECT_ID/$REPO_NAME-docker.$BRANCH_NAME:latest',
'.'
]
# Test auth wrapped docker using KSM key
- name: 'gcr.io/$PROJECT_ID/$REPO_NAME-docker.$BRANCH_NAME:$COMMIT_SHA'
args: [
'build',
'--no-cache',
'--progress=plain',
'--ssh=default=$$SSH_AUTH_SOCK',
#'--network=host',
#'--add-host=metadata.google.internal:127.0.0.1',
'--tag=gcr.io/$PROJECT_ID/$REPO_NAME-git.$BRANCH_NAME:$COMMIT_SHA',
'--tag=gcr.io/$PROJECT_ID/$REPO_NAME-git.$BRANCH_NAME:latest',
'.'
]
dir: 'testdata'
env:
- "PROGRESS_NO_TRUNC=1"
- "DOCKER_BUILDKIT=1"
# Test auth wrapped git using KSM key
- name: 'gcr.io/$PROJECT_ID/$REPO_NAME-git.$BRANCH_NAME:$COMMIT_SHA'
args: ['clone', 'git@github.com:connectedcars/private-module.git']
- name: 'gcr.io/cloud-builders/git'
entrypoint: 'bash'
args: ['-c', 'rm -rf private-module']
#
# Build embedded key auth wrappers
#
Expand All @@ -68,40 +35,30 @@ steps:
args: [
'cp',
'gs://connectedcars-staging-cloudbuilder-private/build.pem',
'./localkey'
'./build.pem'
]
# Build auth wrapper docker image
# Build auth wrapper git image
- name: 'gcr.io/cloud-builders/docker'
dir: localkey
args: [
'build',
'--build-arg=FROM_IMAGE=gcr.io/$PROJECT_ID/$REPO_NAME-docker.$BRANCH_NAME:$COMMIT_SHA',
'--tag=gcr.io/$PROJECT_ID/$REPO_NAME-docker-pemkey.$BRANCH_NAME:$COMMIT_SHA',
'--tag=gcr.io/$PROJECT_ID/$REPO_NAME-docker-pemkey.$BRANCH_NAME:latest', '.'
'--target=git-local',
'--tag=gcr.io/$PROJECT_ID/$REPO_NAME-git-local.$BRANCH_NAME:$COMMIT_SHA',
'--tag=gcr.io/$PROJECT_ID/$REPO_NAME-git-local.$BRANCH_NAME:latest', '.'
]
# Test cloud build wrapper using ssh key embedded in the container
- name: 'gcr.io/$PROJECT_ID/$REPO_NAME-docker-pemkey.$BRANCH_NAME:$COMMIT_SHA'
args: [
'build',
'--no-cache',
'--progress=plain',
'--ssh=default=$$SSH_AUTH_SOCK',
#'--network=host',
#'--add-host=metadata.google.internal:127.0.0.1',
'.'
]
dir: 'testdata'
# Test auth wrapped git using local key
- name: 'gcr.io/$PROJECT_ID/$REPO_NAME-git.$BRANCH_NAME:$COMMIT_SHA'
args: ['clone', 'git@github.com:connectedcars/private-module.git']
secretEnv:
- 'SSH_KEY_PASSWORD'
env:
- "PROGRESS_NO_TRUNC=1"
- "DOCKER_BUILDKIT=1"
- name: 'gcr.io/cloud-builders/git'
entrypoint: 'bash'
args: ['-c', 'rm -rf private-module']
secrets:
- kmsKeyName: projects/connectedcars-staging/locations/global/keyRings/cloudbuilder/cryptoKeys/connectedcars-builder
secretEnv:
SSH_KEY_PASSWORD: CiQAg7wCPfO2Tf9mtZoFWjAtX7whQ481af3gyGdM9WNK26B74UkSUQBefMgeHNh0KTsGybKReXDsFcbmed7f5sw97zSe9cswpKogENM5Ye0jiIu6NfebUpCnmJ9HVHmD/yBknlW4nn1VXBs7HYGiBSFZ52i2HyEopw==
images: [
'gcr.io/$PROJECT_ID/$REPO_NAME.$BRANCH_NAME',
'gcr.io/$PROJECT_ID/$REPO_NAME-git.$BRANCH_NAME',
'gcr.io/$PROJECT_ID/$REPO_NAME-docker.$BRANCH_NAME',
'gcr.io/$PROJECT_ID/$REPO_NAME-docker-pemkey.$BRANCH_NAME'
'gcr.io/$PROJECT_ID/$REPO_NAME-git-local.$BRANCH_NAME'
]
Loading